The decltype(expression) specifier is a type specifier introduced in C++11. With this type specifier, you can get a type that is based on the resultant type of a possibly type-dependent expression.
int i;
static const decltype(i) j = 4;
In this example, decltype(i) is
equivalent to the type name int.const int* g(){
return new int[0];
}
int&& fun(){
int&& var = 1;
return 1;
}
struct A{
double x;
};
template <class T> T tf(const T& t){
return t;
}
bool f(){
return false;
}
struct str1{
template <typename T, typename U>
static decltype((*(T*)0) * (*(U*)0)) mult(const U& arg1, const T& arg2){
return arg1 * arg2;
}
};
template <typename T, typename U> struct str2{
typedef decltype((*(T*)0) + (*(U*)0)) btype;
static btype g(T t, U u);
};
int main(){
int i = 4;
const int j = 6;
const int& k = i;
int&& m = 1;
int a[5];
int *p;
decltype(i) var1; // int
decltype(1) var2; // int
decltype(2+3) var3; // int(+ operator returns an rvalue)
decltype(i=1) var4 = i; // int&, because assignment to int
// returns an lvalue
decltype((i)) var5 = i; // int&
decltype(j) var6 = 1; // const int
decltype(k) var7 = j; // const int&
decltype("decltype") var8 = "decltype"; // const char(&)[9]
decltype(a) var9; // int[5]
decltype(a[3]) var10 = i; // int&([] returns an lvalue)
decltype(*p) var11 = i; // int&(*operator returns an lvalue)
decltype(fun()) var12 = 1; // int&&
decltype(tf(A())) var13; // A
decltype(f()) var14; // bool
decltype((f())) var15; // bool, parentheses around f() are ignored
decltype(f) var16; // bool()
decltype(&f) var17; // bool(*)()
decltype(&A::x) var18; // double A::*
decltype(str1::mult(3.0, 4u)) var19; // double
decltype(str2<float, short>::g(1,3)) var20; // float
decltype(m) var21 = 1; // int&&
decltype((m)) var22 = m; // int&
return 0;
}
In this example, the comment after each decltype statement
explains the type of the defined variable.int func(){
return 0;
}
int func(int a){
return 0;
}
int main(){
int i = 4;
// Incorrect usage. func names an overload function
decltype(func) var1;
// Correct usage. The overload operation is not ambiguous
decltype(func(i)) var2;
return 0;
}
In this example, the compiler issues an error message
because it does not know which func function to match.struct Foo{
int x;
};
int main(){
struct Foo f;
const struct Foo g = {0};
volatile struct Foo* h = &f;
struct Foo func();
decltype(g.x) var1; // int
decltype(h->x) var2; // int
decltype(func().x) var3; // int
return 0;
}
In this example, the constant qualifier of the object
expression g is not desired in the result of decltype(g.x).
Similarly, the volatile qualifier of the pointer expression h is
not desired in the result of decltype(h->x). The
object expression g and the pointer expression h are
lvalues, and the object expression func() is an rvalue,
but they do not affect whether the decltype results of their unparenthesized
member variables are reference types or not.If expression declared in decltype(expression) is a parenthesized nonstatic non-reference class member variable, the constant or volatile type qualifier of the parent object expression or pointer expression of expression contributes to the result of decltype(expression). Similarly, the lvalueness or rvalueness of the object expression or the pointer expression affects the result of decltype(expression).
struct Foo{
int x;
};
int main(){
int i = 1;
struct Foo f;
const struct Foo g = {0};
volatile struct Foo* h = &f;
struct Foo func();
decltype((g.x)) var1 = i; // const int&
decltype((h->x)) var2 = i; // volatile int&
decltype((func().x)) var3 = 1; // int
return 0;
}
In this example, the result of decltype((g.x)) inherits
the constant qualifier of the object expression g.
Similarly, the result of decltype((h->x)) inherits
the volatile qualifier of the pointer expression h.
The object expression g and the pointer expression h are
lvalues, so decltype((g.x)) and decltype((h->x)) are
reference types. The object expression func() is
an rvalue, so decltype((func().x)) is a nonreference
type. If you use the built-in operators .* or ->* within a decltype(expression), the constant or volatile type qualifier of the parent object expression or pointer expression of expression contributes to the result of decltype(expression), regardless of whether expression is a parenthesized or an unparenthesized structure member variable. Similarly, the lvalueness or rvalueness of the object expression or the pointer expression affects the result of decltype(expression).
class Foo{
int x;
};
int main(){
int i = 0;
Foo f;
const Foo & g = f;
volatile Foo* h = &f;
const Foo func();
decltype(f.*&Foo::x) var1 = i; // int&, f is an lvalue
decltype(g.*&Foo::x) var2 = i; // const int&, g is an lvalue
decltype(h->*&Foo::x) var3 = i; // volatile int&, h is an lvalue
decltype((h->*&Foo::x)) var4 = i; // volatile int&, h is an lvalue
decltype(func().*&Foo::x) var5 = 1; // const int, func() is an rvalue
decltype((func().*&Foo::x)) var6 = 1; // const int, func() is an rvalue
return 0;
}
int i = 5;
static const decltype(i++) j = 4; // i is still 5
The
variable i is not increased by 1 outside of the decltype context.template <int N>
struct Foo{
static const int n=N;
};
int i;
decltype(Foo<101>::n,i) var = i; // int&
In this example, Foo template instantiation
occurs, even though var is only determined by the
type of the variable i.int main(){
int i = 5;
int& j = i;
const int k = 1;
volatile int m = 1;
// int&, the redundant & specifier is ignored
decltype(j)& var1 = i;
// const int, the redundant const qualifier is ignored
const decltype(k) var2 = 1;
// volatile int, the redundant volatile qualifer is ignored
volatile decltype(m) var3;
return 0;
}
struct Math{
template <typename T>
static T mult(const T& arg1, const T& arg2){
return arg1 * arg2;
}
};
If arg1 and arg2 are
not the same type, the compiler cannot deduce the return type from
the arguments. You can use the decltype feature to solve this problem,
as shown in the following example:struct Foo{
template<typename T, typename U>
static decltype((*(T*)0)*(*(U*)0)) mult(const T& arg1, const U& arg2)
{
return arg1 * arg2;
}
};
In this example, the return type of the function is
the type of the multiplication result of the two template-dependent
function parameters.__typeof__(int) var1; // okay
decltype(int) var2; // error