は
auto x = initializer;
に相当
decltype(initializer) x = initializer;
また
decltype((initializer)) x = initializer;
それともどちらでもない?
は
auto x = initializer;
に相当
decltype(initializer) x = initializer;
また
decltype((initializer)) x = initializer;
それともどちらでもない?
decltype
また、式がrvalue
またはであるかどうかも考慮しlvalue
ます。
decltype によって示される型は、auto によって推定される型とは異なる場合があります。
#include <vector>
int main()
{
const std::vector<int> v(1);
auto a = v[0]; // a has type int
decltype(v[0]) b = 1; // b has type const int&, the return type of
// std::vector<int>::operator[](size_type) const
auto c = 0; // c has type int
auto d = c; // d has type int
decltype(c) e; // e has type int, the type of the entity named by c
decltype((c)) f = c; // f has type int&, because (c) is an lvalue
decltype(0) g; // g has type int, because 0 is an rvalue
}
それは重要な違いをほとんど説明しています。注意decltype(c)
しdecltype((c))
て、同じではありません!
また、次の例のように、協力的な方法で一緒に動作することもauto
あります ( wikiから取得し、少し変更しています)。decltype
int& foo(int& i);
float foo(float& f);
template <class T>
auto f(T& t) −> decltype(foo(t))
{
return foo(t);
}
ウィキペディアは、次のようにセマンティクスをさらに説明してdecltype
います。
sizeof 演算子と同様に、decltype のオペランドは評価されません。非公式に、decltype(e) によって返される型は次のように推定されます。
- 式 e がローカルまたは名前空間スコープ内の変数、静的メンバー変数、または関数パラメーターを参照する場合、結果はその変数またはパラメーターの宣言された型になります。
- e が関数呼び出しまたはオーバーロードされた演算子呼び出しである場合、 decltype(e) はその関数の宣言された戻り値の型を示します
- それ以外の場合、e が左辺値の場合、decltype(e) は T& です。ここで、T は e の型です。e が右辺値の場合、結果は T
これらのセマンティクスは、ジェネリック ライブラリの作成者のニーズを満たすと同時に、初心者のプログラマーにとって直感的になるように設計されています。これは、decltype の戻り値の型が、ソース コードで宣言されているオブジェクトまたは関数の型と常に正確に一致するためです。より正式には、ルール 1 は括弧で囲まれていない id 式とクラス メンバー アクセス式に適用されます。関数呼び出しの場合、推定される型は、オーバーロード解決の規則によって決定される、静的に選択された関数の戻り値の型です。例:
const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1; // type is const int&&
decltype(i) x2; // type is int
decltype(a->x) x3; // type is double
decltype((a->x)) x4; // type is const double&
decltype の後の 2 つの呼び出しの違いの理由は、括弧で囲まれた式 (a->x) が ID 式でもメンバー アクセス式でもないため、名前付きオブジェクトを示さないためです。 、その推定型は「式の型への参照」、または const double& です。
これは機能しません(そして醜いです):
decltype([]() { foo(); }) f = []() { foo(); };
一方
auto f = []() { foo(); };
意思。
場合によります。auto
異なる目的をdecltype
果たすため、1 対 1 でマッピングされません。
のルールauto
は、テンプレート パラメーターの推論と同じであるため、説明が最も簡単です。ここでは詳しく説明しませんが、auto&
とauto&&
もいくつかの可能な用途であることに注意してください。
decltype
ただし、いくつかのケースがあり、そのうちのいくつかは上で説明したものです (n3290、7.1.6.2 単純型指定子 [dcl.type.simple] から取得した情報と引用)。これを 2 つのカテゴリに分けます。
非公式に言えば、それは名前(最初のケース) または式decltype
のどちらでも操作できると思います。(形式的には、文法に従って式に作用するため、最初のケースは洗練されたもの、2 番目のケースはキャッチオールと考えてください。)decltype
decltype で名前を使用すると、そのエンティティの宣言された型が取得されます。たとえばdecltype(an_object.a_member)
、クラス定義に表示されるメンバーの型です。一方、使用すると、キャッチオールのケースになり、 code に表示されるdecltype( (an_object.a_member) )
式の型を調べています。
したがって、質問のすべてのケースをカバーする方法:
int initializer;
auto x = initializer; // type int
// equivalent since initializer was declared as int
decltype(initializer) y = initializer;
enum E { initializer };
auto x = initializer; // type E
// equivalent because the expression is a prvalue of type E
decltype( (initializer) ) y = initializer;
struct {
int const& ializer;
} init { 0 };
auto x = init.ializer; // type int
// not equivalent because declared type is int const&
// decltype(init.ializer) y = init.ializer;
// not equivalent because the expression is an lvalue of type int const&
// decltype( (init.ializer) ) y = init.ializer;
auto
auto
単純です: 値によるテンプレート パラメーター推定と同じ型が得られます。auto
式に対して均一に機能します。
template <class T>
void deduce(T x);
int &refint();
std::string str();
std::string const conststr();
auto i1 = 1; // deduce(1) gives T=int so int i1
auto i2 = i1; // deduce(i1) gives T=int so int i2
auto i3 = refint(); // deduce(refint()) gives T=int so int i3
const auto ci1 = i1; // deduce(i1) gives T=int so const int ci1
auto i4 = ci1; // deduce(ci1) gives T=int so int i4
auto s1 = std::string(); // std::string s1
auto s2 = str(); // std::string s2
auto s3 = conststr(); // std::string s3
C++ 式では、参照型を持つことはできません ( refint()
has type int
not int&
)。
式の左辺値は、式の右側 (等号の右側、または一般的にコピーされているもの) では問題にならないことに注意してください。右辺値は、左辺値および1
のように扱われます。i1
refint()
値パラメーター (つまり、非参照パラメーター) の場合、左辺値から右辺値への変換だけでなく、配列からポインターへの変換も適用されます。const は無視されます。
decltype
decltype
恐ろしいインターフェースを備えた非常に便利な機能です。
decltype
名前検索と他の式で定義された一部の式では、動作が異なります! これは、人々が C++ を嫌うタイプの機能です。
decltype
と名付けられたもののdecltype(entity)
名前の検索を行い、エンティティの宣言された型を提供します。(entity
非修飾識別子または修飾識別子、または などのメンバー アクセスを指定できますexpr.identifier
。)
decltype(f(args))
名前の検索とオーバーロードの解決を行い、式の型ではなく、宣言された関数の戻り値の型を与えます。
extern decltype(refint()) ri1; // int &ri1
これで、次の方法で言語の理解を確認できますdecltype
。
template <class T, class U>
struct sametype {};
template <class T>
struct sametype<T,T> {typedef int same;};
sametype<T,U>::same
が存在し、まったく 同じタイプです。T
U
sametype<decltype (i1), int>::same check_i1;
sametype<decltype (i2), int>::same check_i2;
sametype<decltype (i3), int>::same check_i3;
sametype<decltype (i4), int>::same check_i4;
sametype<decltype (ci1), const int>::same check_ci1;
sametype<decltype (ir1), int&>::same check_ir1;
sametype<decltype (s1), std::string>::same check_s1;
sametype<decltype (s2), std::string>::same check_s2;
sametype<decltype (s3), std::string>::same check_s3;
うまくコンパイルされるので、私は間違っていませんでした!
decltype
他の表現のそれ以外の場合、 whereexpr
は名前ルックアップに関して定義されていません (上記のケースのいずれでもない)、式 など、別個の機能(expr)
をdecltype
実装します (ただし、C++ 設計者はそれに別のキーワードを費やさなかったでしょう)。
decltype(expr)
lxrvalueness (lvalue/xvalue/prvalue-ness) で装飾された式の型を与えます:
T
はT
T
はT&&
T
与えるT&
これは、関数呼び出しルールの逆数です: if f
is a function with return type
T&
、式f()
は左辺値ですT&&
、式f()
は xvalue ですT
、式f()
は prvalue ですまた、キャストの場合: ネイキッド タイプ (純粋なオブジェクト タイプ、非参照) の場合T
(T&)expr
左辺値です(T&&)expr
x値です(T)expr
プリバリューです参照性は、lxrvalueness を型にエンコードすることです。decltype
物事のlxrvaluenessを保存して転送するためにこのエンコーディングを行います。
これは、式に別名を付けたい場合に便利です。expr
関数呼び出し (通常f(args)
または のような演算子構文) である式の lxrvalueness は、宣言されa @ b
た as の lxrvalueness と同じです。alias()
decltype(expr) alias();
これは、汎用コードでの純粋な転送に使用できます。
// decorated type of an expression
#define EXPR_DEC_TYPE(expr) decltype((expr))
int i;
int &ri = i;
int fi();
int &fri();
EXPR_DEC_TYPE(i) alias_i = i; // int &
EXPR_DEC_TYPE(ri) alias_ri = ri; // int &
EXPR_DEC_TYPE(fi()) alias_fi(); // int alias_fi()
EXPR_DEC_TYPE(fri()) alias_fri(); // int &alias_fri()
EXPR_DEC_TYPE(foo())
設計上 (ほとんどの場合)等しいことに注意してくださいdeclexpr(foo())
。ただし、計算は異なります。
declexpr(foo(args))
の名前検索を行いfoo
、オーバーロードの解決を行い、宣言を見つけ、宣言された正確な戻り値の型を返し、話の終わり
EXPR_DEC_TYPE(foo(args))
宣言の型を見つけて計算します
T
戻り値の型がネイキッドである式の型 (参照なし
)
宣言された戻り値の型の参照性に応じた式の lxrvalueness LXR : 参照の左辺値、r-参照の xvalue...
次に、その型T
をLXRで修飾して型を取得しdecT
ます。
decT
LXR = prvalueのT
場合decT
LXR = xvalueのT&&
場合decT
LXR = 左辺値のT&
場合EXPR_DEC_TYPE
宣言された戻りdecT
値の型と同じ戻り値。
initializer
が配列の場合、decltype(x)
またはdecltype((x))
単純に機能しない場合。ただしauto
、ポインターに推測されます。initializer
、適用decltype(fp)
すると関数の型auto
が推測されますが、戻り値の型が推測されます。したがって、一般的には、あなたが求めたバージョンauto
の代わりとは見なされません。decltype()