Reputation: 51
when reading about auto and decltype (C++11) i see the below function
auto findMin(A a, B b) -> decltype(a < b ? a : b)
{
return (a < b) ? a : b;
}
this part -> decltype(a < b ? a : b) is strange to me. what kind of function declaration or it just applies for auto and decltype only ?
Upvotes: 5
Views: 1211
Reputation: 1294
decltype()
is different from the auto keyword
. We declare a variable in auto
and we have to give it a first value.
decltype()
corresponds to a type.
There are 2 separate sets of rules for decltype()
.
1) decltype(name)
Examples ;
decltype(x)
decltype(x.y)
decltype(ptr->a)
/*The compiler will look at what the type for this name is and think that-
the type obtained with decltype is that type.*/
int ival; decltype(ival) x; -----> int x;
double dval; decltype(dval) x; -----> double x;
struct Data{
int mx;
};
Data data; decltype(data.mx) x; -----> int x;
Data *p=&data; decltype(p->mx) x; -----> int x;
Note : We can use decltype
wherever we use type because decltype means type.
Note: const
is ignored in auto keyword
, but const
is not ignored in decltype
.
const int cx=10; decltype(cx) y=10; -----> const int y=10;
Note: reference(&)
is ignored in auto keyword
, but reference(&)
is not ignored in decltype
.
int x=10;
int &r=x;
decltype(r) foo(); -----> int& foo();
int x=10;
const int &r=x;
decltype(r) y=x; -----> const int& y;
Note:No array to pointer conversion (array decay) in decltype
.
int a[] {1,2,3,4};
decltype(a) b; -----> int b[4];
2) decltype(expression)
decltype (x) -----> 1. Rule set
decltype ((x)) -----> 2. Rule set
All of the examples below will apply the 2nd rule set, so it's no longer a name but an expression
.
decltype (10);
decltype (x+5);
decltype (x++);
decltype ((y));
decltype (*ptr);
decltype (a[5]);
Note: In this rule set, type inference depends on whether the expression is Lvalue
, Rvalue
, or Xvalue
.
Note: If the expression is a PRValue expression
, the resulting type is directly the type of that expression.
decltype(10) x; // x is int.
int x=20;
decltype(x+4.5) dval; // dval is double.
Note: If the operand of the decltype
operator is not a name,if expression is in value category PRvalue expression
, the type obtained with decltype
is type T
.If the expression is in the Lvalue expression
value category, the type obtained with decltype is T&
.If the expression is in the Xvalue expression
value category, the type obtained with decltype is T&&
.
If the expression is an Lvalue expression, examples of its status are given below.
int x=20;
decltype(++x) y=x; -----> int & y;
int x=20;
int *ptr=&x;
decltype(*ptr) y=x; -----> int & y;
int a[10] [20] {};
decltype (a[2][5]) ----->int &;
int x=10;
decltype(x) -----> int
decltype((x)) -----> Since the variable x is an Lvalue, the type of the expression is int&.
Note: The compiler does not generate an opcode for the expression that is the operand of the decltype operator
int x=20;
decltype(x++) y=45; -----> y is int.
decltype(++x) y=x; -----> Since ++x is an Lvalue y is int&. x=20 because the compiler does not generate an opcode for the expression that is the operand of the decltype operator
Upvotes: 2
Reputation: 1261
C++11 allows two ways to declare a function's return type
return_type function_name(arg1_type arg1, arg2_type arg2);
Ex. int bar(int x, int y);
auto function_name(arg1_type arg1, arg2_type arg2) -> return_type;
Ex. auto foo(int x, int y) -> int
The first way is called 'prefix return type'. The second one, obviously is called 'suffix return type'. The 'suffix return type' is not just another fancy way to specify return types. It plays very important when doing generic programming.
Let me explain how.
'auto' type specifier is a great feature provided by C++11 which lets you define variables whose type can be deduced by the compiler. But there is a catch. The programmer needs to help the compiler deduce the type of an 'auto' variable using an initializer. For example, in case of
auto x = 100;
the compiler deduces type of x to be 'int' or in case of
auto y{'t'};
the compiler deduces type of y to be 'list of char'
Compiler's job of deducing the type is simpler when the initializer's type is simple and already known like above. But it is not always that simple. Sometimes, the data type of auto variable or return value is dependent on the type / computation of other variables. For example, in case of a template function, the return type needs to be determined based on the data type of input variables and/or the computation.
That is when decltype() specifier comes into the picture.
decltype(expr)
As it's name suggests, declaration type specifier, deduces type of an expression fed to it.
A rule of thumb I forgot to mention before, a compiler parses an expression left to right.
template<typename X, typename Y>
auto mul(X a, Y b) -> decltype(a*b)
Here, the compiler sees auto as a placeholder for the type that 'decltype' takes responsibility of deducing based on the expression it is fed.
Upvotes: 1
Reputation: 10998
this part -> decltype(a < b ? a : b) is strange to me. what kind of function declaration or it just applies for auto and decltype only ?
The arrow ->
following auto
and the parenthesis, denotes a trailing return type, and it allows you to specify the return type after knowing the function parameters - more freedom if you will.
decltype
gives us the declared type and this allows us to inspect the type of an expression.
In the ternary expression a < b ? a : b
, the type of the expression is the common type between a
and b
. That is, for int
and double
the common type is double
as int
promotes to double
.
Calling decltype
with the mentioned expression decltype(a < b ? a : b)
then gives us the common type that will be the type of the return value that findMin
returns. The function still needs to return a value and so we see the expression there as well.
P.s the common type behaviour of the ternary expression is captured by the std::common_type
trait (since c++11)
Upvotes: 4