プログラミング言語C++における decltype指定子(decltype-specifier) の振る舞いについてメモ。
int x = 42; // 括弧なしの変数名 x decltype( x ) y = x; // int 型 // 括弧付きの式 (x) decltype((x)) z = x; // int& 型
一見すると奇妙に思えるdecltype(e)
型導出ルールは、「式e
の型宣言を探す」という指針に基づいている。
- 変数名
x
:ソースコード上に直接登場する変数x
宣言時の型int
となる。 - 式
(x)
:式(x)
はソースコード上の宣言には現れない。式(x)
の左辺値としての性質(lvalueness)を保持する参照型int&
となる。*1
C++11言語仕様へのdecltype導入当時の提案文書(PDF) N2115 Decltype (revision 6): proposed wording より一部引用。*2
2.2 Semantics of decltype
Determining the typedecltype(e)
build on a single guiding principle: look for the declared type of the expressione
. Ife
is a variable or formal parameter, or a function/operator invocation, the programmer can trace down the variable's, parameter's, or function's declaration, and find the type declared for the particular entity directly from the program text. This type is the result ofdecltype
. For expressions that do not have a declaration in the program text, such as literals and calls to built-in operators, lvalueness implies a reference type.
おまけ:括弧付きの変数名のみからなる式(e)
の扱いは検討当時も紆余曲折あったようで、(PDF)N1705(rev.4)時点ではdecltype((e))
とdecltype(e)
は等価とされていたが、その後のN2115(rev.6)にて現行仕様へと再変更されている。
C++17 10.1.7.2/p4より引用(下線部は強調)。*3
For an expression
e
, the type denoted bydecltype(e)
is defined as follows:
- if
e
is an unparenthesized id-expression naming a structured binding (11.5),decltype(e)
is the referenced type as given in the specification of the structured binding declaration;- otherwise, if
e
is an unparenthesized id-expression or an unparenthesized class member access (8.2.5),decltype(e)
is the type of the entity named bye
. If there is no such entity, or ife
names a set of overloaded functions, the program is ill-formed;- otherwise, if
e
is an xvalue,decltype(e)
isT&&
, whereT
is the type ofe
;- otherwise, if
e
is an lvalue,decltype(e)
isT&
, whereT
is the type ofe
;- otherwise,
decltype(e)
is the type ofe
.The operand of the decltype specifier is an unevaluated operand (Clause 8).
[Example:const int&& foo(); int i; struct A { double x; }; const A* a = new A(); decltype(foo()) x1 = 17; // type is const int&& decltype(i) x2; // type is int decltype(a->x) x3; // type is double decltype((a->x)) x4 = x3; // type is const double&-- end example] [Note: The rules for determining types involving
decltype(auto)
are specified in 10.1.7.4. --end note]
関連URL
- 本の虫: decltypeの二重括弧が参照になる理由
- 本の虫: decltypeの訓練
- decltypeの不思議
- cppreference: decltype specifier, cpprefjp: decltype
*1:C++文法上は変数宣言時に括弧を記述できるが(例:int (y) = 42;)、この冗長な括弧は単に無視されてdecltypeの振る舞いには影響を与えない。https://gist.github.com/yohhoy/7b63eafcc42e078f34294857698adfd9
*2:C++仕様には後続文書(PDF)N2343が採択されている。
*3:1番目のBulletは 構造化束縛(structured binding) に対するルール。構造化束縛で導入される名前の型導出は、通常の変数宣言よりも複雑な規則となっている。