1. ホーム
  2. c++

[解決済み] decltype(auto)の使い方にはどのようなものがありますか?

2022-04-20 10:54:54

質問

c++14では decltype(auto) イディオムが導入されています。

通常、次のような用途に使用されます。 許す auto の宣言で decltype のルールは、与えられた式に対して .

イディオムの良い使い方の例を探すと、次のようなものしか思いつきません(by. スコット・マイヤーズ )、すなわち 関数の戻り値の推論 :

template<typename ContainerType, typename IndexType>                // C++14
decltype(auto) grab(ContainerType&& container, IndexType&& index)
{
  authenticateUser();
  return std::forward<ContainerType>(container)[std::forward<IndexType>(index)];
}

この新しい言語機能が役立つ他の例はありますか?

どのように解決するのですか?

汎用コードでの戻り値の転送

最初にあげた例のように、非一般的なコードでは、戻り値の型として参照を取得するように手動で選択することができます。

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 auto specifier [dcl.spec.auto] (オートスペシフィケーション)

1 その autodecltype(auto) はプレースホルダを指定します。 の型は、後で置き換えられることになります。 イニシャライザ、またはtrailing-return-typeで明示的に指定します。 そのため auto 型指定子もラムダが 一般的なラムダです。

2 プレースホルダ型 が表示されることがあります。 の関数宣言子で、dec-specifier-seq, type-specifier-seq, conversion-function-id、または trailing-return-type。 そのような宣言子が有効であるあらゆるコンテキストにおいて . 関数 宣言子には 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)