decltype(expression) 型指定子 (C++11)
decltype(expression) 指定子は、C++11 に導入される型指定子です。この型指定子を使用すると、型に依存する可能性のある式の結果の型に基づいて型を取得できます。
int i;
static const decltype(i) j = 4;
この例では、decltype(i) は型名 int と等価です。
decltype の一般的な使用規則
- expression が、括弧で囲まない ID 式またはクラス・メンバーである場合、decltype(expression) は、expression によって命名されるエンティティーの型です。そのようなエンティティーが存在しないか、または expression が多重定義関数のセットを命名する場合、プログラムは不適格になります。
- 上記以外の場合に、expression が関数呼び出しまたは多重定義演算子 (expression の前後の括弧は無視されます) の呼び出しである場合、decltype(expression) は静的に選択される関数の戻りの型です。
- 上記以外の場合に、expression が左辺値である場合、decltype(expression) は、T& です。ここで、T は expression の型です。
- 上記以外の場合、decltype(expression) は expression の型です。
const int* bar(){
return new int[0];
}
struct A{
double x;
};
template <class T> T tFoo(const T& t){
return t;
}
bool func(){
return false;
}
struct Foo{
template <typename T, typename U>
static decltype((*(T*)0) * (*(U*)0)) foo(const U& arg1, const T& arg2){
return arg1 * arg2;
}
};
template <typename T, typename U> struct Bar{
typedef decltype((*(T*)0) + (*(U*)0)) btype;
static btype bar(T t, U u);
};
int main(){
int i = 4;
const int j = 6;
const int& k = i;
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") var10 = "decltype"; // const char(&)[9]
decltype(a) var8; // int[5]
decltype(a[3]) var9 = i; // int&([] returns an lvalue)
decltype(*p) var11 = i; // int&(*operator returns an lvalue)
decltype(tFoo(A())) var12; // A
decltype(func()) var13; // bool
decltype((func())) var14; // bool, parentheses around f() are ignored
decltype(func) var15; // bool()
decltype(&func) var16; // bool(*)()
decltype(&A::x) var17; // double A::*
decltype(Foo::foo(3.0, 4u)) var18; // double
decltype(Bar<float, short>::bar(1,3)) var19; // float
return 0;
}
この例では、各 decltype ステートメントの後のコメントで、定義される変数の型を説明しています。
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;
}
この例では、コンパイラーは、一致する func 関数を認識できないため、エラー・メッセージを出します。
decltype を構造体メンバー変数と共に使用する場合の規則
- オブジェクト式またはポインター式が、定数または volatile 修飾子を使用して指定される場合、型修飾子は decltype(expression) の結果を導きません。
- オブジェクト式またはポインター式の左辺値または右辺値は、decltype(expression) が参照型かどうかということに影響を与えません。
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;
}
この例では、オブジェクト式 g の定数修飾子は、decltype(g.x) の結果では望まれません。 同様に、ポインター式 h の volatile 修飾子は、decltype(h->x) の結果では望まれません。オブジェクト式 g およびポインター式 h は左辺値であり、オブジェクト式 func() は右辺値ですが、これらは括弧で囲まないメンバー変数の decltype の結果が参照型かどうかということには影響を与えません。
decltype(expression) で宣言される expression が、括弧で囲む構造体メンバー変数である場合、expression の親オブジェクト式または親ポインター式の定数または volatile 型修飾子は、decltype(expression) の結果に反映されます。同様に、オブジェクト式またはポインター式の左辺値または右辺値は、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;
}
この例では、decltype((g.x)) の結果は、オブジェクト式 g の定数修飾子を継承します。 同様に、decltype((h->x)) の結果は、ポインター式 h の volatile 修飾子を継承します。 オブジェクト式 g およびポインター式 h は左辺値であるため、decltype((g.x)) および decltype((h->x)) は参照型です。オブジェクト式 func() は右辺値であるため、decltype((func().x)) は非参照型です。
組み込み演算子 .* または ->* を decltype(expression) 内で使用する場合、expression の親オブジェクト式または親ポインター式の定数または volatile 型修飾子は、expression が括弧で囲まれているか、または括弧で囲まない構造体メンバー変数であるかに関係なく、decltype(expression) の結果を導きます。同様に、オブジェクト式またはポインター式の左辺値または右辺値は、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;
}
副次作用および decltype
decltype(expression) を使用して型を取得する場合、decltype の括弧付きのコンテキストで追加命令を実行できますが、decltype コンテキストの外側には副次作用はありません。
int i = 5;
static const decltype(i++) j = 4; // i is still 5
変数 i は、decltype コンテキストの外側では 1 増加しません。
template <int N>
struct Foo{
static const int n=N;
};
int i;
decltype(Foo<101>::n,i) var = i; // int&
この例では、var が変数 i の型によってのみ決定される場合でも、Foo テンプレートのインスタンス生成が行われます。
decltype での冗長修飾子および指定子
- 定数修飾子
- volatile 修飾子
- & 指定子
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;
}
IBM 拡張の始まり。
__ptr64 および __ptr128 は、ポインター型のサイズを指定するために使用されるポインター属性指定子です。decltype(expression) で宣言された expression がポインター型である場合、ポインター属性指定子は decltype(expression) の結果では無視されます。何らかのポインター属性が decltype(expression) に指定されている場合、同じ宣言内でポインター属性が重複することになり、エラーと診断されます。
int * ptr;
decltype(ptr) ptr1; //ptr1 has the type "int *"
decltype(ptr) __ptr64 ptr2; //error, __ptr64 is unexpected
int * __ptr64 ptr3;
decltype(ptr3) ptr4; //ptr4 has the type "int * __ptr64"
decltype(ptr3) __ptr128 ptr5; //error, __ptr128 is unexpected
IBM 拡張の終わり。
テンプレート従属名および decltype
struct Math{
template <typename T>
static T mult(const T& arg1, const T& arg2){
return arg1 * arg2;
}
};
struct Foo{
template<typename T, typename U>
static decltype((*(T*)0)*(*(U*)0)) mult(const T& arg1, const U& arg2)
{
return arg1 * arg2;
}
};
この例では、関数の戻りの型は、テンプレートに依存する 2 つの関数仮パラメーターの乗算結果の型です。
typeof 演算子および decltype
decltype 機能は、既存の typeof 機能に類似しています。
これらの 2 つの機能間の 1 つの違いは、decltype はオペランドとして式を受け入れるのに対して、typeof は型名も受け入れることができる点です。次の例を検討してみます。__typeof__(int) var1; // okay
decltype(int) var2; // error
この例では、int は型名であるため、decltype のオペランドとしては無効です。
関連情報