一般的なコードでの戻り型の転送
非ジェネリックコードの場合、最初の例のように、参照を戻り値の型として取得するように手動で選択できます。
auto const& Example(int const& i)
{
return i;
}
しかし、汎用コードでは、参照を処理するのか値を処理するのかを知らなくても、戻り値の型を完全に転送できるようにしたいとします。decltype(auto)
あなたにその能力を与えます:
template<class Fun, class... Args>
decltype(auto) Example(Fun fun, Args&&... args)
{
return fun(std::forward<Args>(args)...);
}
再帰的なテンプレートでの戻り型の推定の遅延
ではこのQ&Aテンプレートの戻り値の型は次のように指定されたときに、数日前に、テンプレートのインスタンス中に無限再帰が発生しましたdecltype(iter(Int<i-1>{}))
代わりにdecltype(auto)
。
template<int i>
struct Int {};
constexpr auto iter(Int<0>) -> Int<0>;
template<int i>
constexpr auto iter(Int<i>) -> decltype(auto)
{ return iter(Int<i-1>{}); }
int main() { decltype(iter(Int<10>{})) a; }
decltype(auto)
ここでは、テンプレートのインスタンス化のほこりが落ち着いた後、戻り型の控除を遅らせるために使用されます。
その他の用途
decltype(auto)
他のコンテキストで使用することもできます。たとえば、標準N3936ドラフトも述べています。
7.1.6.4自動指定子[dcl.spec.auto]
1 auto
and decltype(auto)
型指定子は、初期化子からの推論によって、またはトレーリングリターン型を使用した明示的な指定によって、後で置き換えられるプレースホルダー型を指定します。auto
型特異的ERは、ラムダは、一般的なラムダであることを意味するために使用されます。
2プレースホルダータイプは、decl-specifier-seq、type-specifier-seq、conversion-function-id、またはtrailing-return-typeの関数宣言子とともに、そのような宣言子が有効な任意のコンテキストで使用できます。関数宣言子にトレーリングリターンタイプ(8.3.5)が含まれている場合は、関数の宣言された戻りタイプを指定します。関数の宣言された戻り値の型にプレースホルダー型が含まれている場合、関数の戻り値の型は、関数の本体にあるreturnステートメント(ある場合)から推定されます。
ドラフトには、変数の初期化の次の例も含まれています。
int i;
int&& f();
auto x3a = i; // decltype(x3a) is int
decltype(auto) x3d = i; // decltype(x3d) is int
auto x4a = (i); // decltype(x4a) is int
decltype(auto) x4d = (i); // decltype(x4d) is int&
auto x5a = f(); // decltype(x5a) is int
decltype(auto) x5d = f(); // decltype(x5d) is int&&
auto x6a = { 1, 2 }; // decltype(x6a) is std::initializer_list<int>
decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression
auto *x7a = &i; // decltype(x7a) is int*
decltype(auto)*x7d = &i; // error, declared type is not plain decltype(auto)