35

auto x = initializer;

に相当

decltype(initializer) x = initializer;

また

decltype((initializer)) x = initializer;

それともどちらでもない?

4

5 に答える 5

37

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) −&gt; 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& です。

于 2011-07-29T07:32:40.767 に答える
9

これは機能しません(そして醜いです):

decltype([]() { foo(); }) f = []() { foo(); };

一方

auto f = []() { foo(); };

意思。

于 2011-07-29T08:07:52.100 に答える
6

場合によります。auto異なる目的をdecltype果たすため、1 対 1 でマッピングされません。

のルールautoは、テンプレート パラメーターの推論と同じであるため、説明が最も簡単です。ここでは詳しく説明しませんが、auto&auto&&もいくつかの可能な用途であることに注意してください。

decltypeただし、いくつかのケースがあり、そのうちのいくつかは上で説明したものです (n3290、7.1.6.2 単純型指定子 [dcl.type.simple] から取得した情報と引用)。これを 2 つのカテゴリに分けます。

  • 標準が「括弧で囲まれていないid式または括弧で囲まれていないクラスメンバーアクセス」と呼んでいるものを使用する場合
  • 残り!

非公式に言えば、それは名前(最初のケース) または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;
于 2011-07-29T07:50:33.500 に答える
2

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 intnot int&)。

式の左辺値は、式の右側 (等号の右側、または一般的にコピーされているもの) では問題にならないことに注意してください。右辺値は、左辺値および1のように扱われます。i1refint()

値パラメーター (つまり、非参照パラメーター) の場合、左辺値から右辺値への変換だけでなく、配列からポインターへの変換も適用されます。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が存在し、まったく 同じタイプです。TU

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) で装飾された式の型を与えます:

  • タイプの prvalue (純粋な右辺値)TT
  • タイプの xvalueTT&&
  • 型の左辺値がT与えるT&

これは、関数呼び出しルールの逆数です: if fis a function with return type

  • T&、式f()は左辺値です
  • T&&、式f()は xvalue です
  • 裸の型 (純粋なオブジェクト型、非参照) T、式f()は prvalue です

また、キャストの場合: ネイキッド タイプ (純粋なオブジェクト タイプ、非参照) の場合T

  • (T&)expr左辺値です
  • (T&&)exprx値です
  • (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...

    次に、その型TLXRで修飾して型を取得しdecTます。

    • decTLXR = prvalueのT場合
    • decTLXR = xvalueのT&&場合
    • decTLXR = 左辺値のT&場合

    EXPR_DEC_TYPE宣言された戻りdecT値の型と同じ戻り値。

于 2015-08-15T08:56:06.730 に答える
0
  1. Ifinitializerが配列の場合、decltype(x)またはdecltype((x)) 単純に機能しない場合。ただしauto、ポインターに推測されます。
  2. が関数の場合initializer、適用decltype(fp)すると関数の型autoが推測されますが、戻り値の型が推測されます。

したがって、一般的には、あなたが求めたバージョンautoの代わりとは見なされません。decltype()

于 2011-07-29T07:36:06.950 に答える