decltype (C++)

decltype 型指定子は、指定された式の型を生成します。 decltype 型指定子は、auto キーワードと共に使用され、主にテンプレート ライブラリを記述する開発者にとって有益です。 戻り値の型がテンプレート引数の型に依存する関数テンプレートを使用 auto して decltype 宣言します。 または、別の関数の呼び出しをラップしdecltype、ラップされた関数の戻り値の型を返す関数テンプレートを使用autoして宣言します。

構文

decltype( expression )

パラメーター

expression
式。 詳細については、「」をご覧ください。

戻り値

expression パラメーターの型。

解説

decltype 型指定子は、Visual Studio 2010 以降のバージョンでサポートされ、ネイティブ コードまたはマネージド コードと共に使用できます。 decltype(auto) (C++14) は Visual Studio 2015 以降でサポートされています。

コンパイラは、次の規則を使用してパラメーターの型を expression 決定します。

  • パラメーターがexpression識別子またはクラス メンバー アクセスである場合はexpressiondecltype(expression). このようなエンティティがない場合、またはパラメーターに expression オーバーロードされた関数のセットの名前が付いている場合、コンパイラはエラー メッセージを生成します。

  • パラメーターが expression 関数またはオーバーロードされた演算子関数の呼び出しである場合は、 decltype(expression) 関数の戻り値の型です。 オーバーロードされた演算子を囲んでいるかっこは無視されます。

  • パラメーターがexpression右辺値場合、decltype(expression)の型expressionです。 パラメーターがexpression左辺値場合は、decltype(expression)次のexpression型への左辺値参照です。

decltype 型指定子のいくつかの使用方法を次のコード例に示します。 まず、次のステートメントをコーディングしたとします。

int var;
const int&& fx();
struct A { double x; };
const A* a = new A();

次に、次の表の 4 つの decltype ステートメントによって返される型をチェックします。

ステートメント Notes
decltype(fx()); const int&& const int への右辺値参照
decltype(var); int 変数 var の型。
decltype(a->x); double メンバー アクセスの型。
decltype((a->x)); const double& 内側のかっこは、ステートメントをメンバー アクセスではなく式として評価します。 そして、aconst ポインターとして宣言されているため、型は const double への参照です。

decltype および auto

C++14 では、後続の戻り値の型なしで使用 decltype(auto) して、戻り値の型がテンプレート引数の型に依存する関数テンプレートを宣言できます。

C++11 では、後続のdecltype戻り値の型指定子をキーワード (keyword)とauto共に使用して、戻り値の型がそのテンプレート引数の型に依存する関数テンプレートを宣言できます。 たとえば、関数テンプレートの戻り値の型がテンプレート引数の型に依存する次のコード例を考えてみましょう。 コード例では、プレースホルダーは UNKNOWN 戻り値の型を指定できないことを示しています。

template<typename T, typename U>
UNKNOWN func(T&& t, U&& u){ return t + u; };

型指定子の decltype 導入により、開発者は関数テンプレートから返される式の型を取得できます。 late-specified 戻り型を宣言するには、後述する代替関数宣言構文auto キーワード、および decltype 型指定子を使用します。 遅延指定の戻り値の型は、宣言がコード化されたときではなく、コンパイル時に決定されます。

次のプロトタイプは代替関数宣言の構文について説明します。 修飾子 constvolatile 例外の throw指定 は省略可能です。 プレースホルダーは function_body 、関数の動作を指定する複合ステートメントを表します。 ベスト コーディングの方法として、ステートメント内のexpressiondecltypeプレースホルダーは、ステートメントでreturn指定された式 (存在するfunction_body場合) と一致する必要があります。

autofunction_name(parametersopt)constopt opt optexpression)decltype(->noexceptvolatile{function_body};

次のコード例では、関数テンプレートの遅延指定された戻り値のmyFunc型は、引数とuテンプレート引数のt型によって決まります。 コーディングの推奨手順として、コード例では、右辺値の参照と完全転送をサポートする forward 関数テンプレートも使用しています。 詳細については、「右辺値参照宣言子: &>」を参照してください

//C++11
template<typename T, typename U>
auto myFunc(T&& t, U&& u) -> decltype (forward<T>(t) + forward<U>(u))
        { return forward<T>(t) + forward<U>(u); };

//C++14
template<typename T, typename U>
decltype(auto) myFunc(T&& t, U&& u)
        { return forward<T>(t) + forward<U>(u); };

decltype 関数と転送関数 (C++11)

転送関数は、他の関数の呼び出しをラップします。 関数テンプレートで、引数、またはそれらの引数を含む式の結果を別の関数に転送する場合を考えます。 さらに、転送関数は、他の関数を呼び出した結果を返します。 このシナリオでは、転送関数の戻り値の型は、ラップされた関数の戻り値の型と同じである必要があります。

このシナリオでは、型指定子なしで適切な型式を decltype 記述することはできません。 型指定子は decltype 、関数が参照型を返すかどうかに関する必要な情報を失わないので、ジェネリック転送関数を有効にします。 転送関数のコード例については、前 myFunc の関数テンプレートの例を参照してください。

次のコード例では、関数テンプレート Plus()の遅延指定された戻り値の型を宣言します。 Plus の関数は operator+ オーバーロードを持つ 2 つのオペランドを処理します。 したがって、プラス演算子 (+) と関数の戻り値の型の Plus 解釈は、関数引数の型によって異なります。

// decltype_1.cpp
// compile with: cl /EHsc decltype_1.cpp

#include <iostream>
#include <string>
#include <utility>
#include <iomanip>

using namespace std;

template<typename T1, typename T2>
auto Plus(T1&& t1, T2&& t2) ->
   decltype(forward<T1>(t1) + forward<T2>(t2))
{
   return forward<T1>(t1) + forward<T2>(t2);
}

class X
{
   friend X operator+(const X& x1, const X& x2)
   {
      return X(x1.m_data + x2.m_data);
   }

public:
   X(int data) : m_data(data) {}
   int Dump() const { return m_data;}
private:
   int m_data;
};

int main()
{
   // Integer
   int i = 4;
   cout <<
      "Plus(i, 9) = " <<
      Plus(i, 9) << endl;

   // Floating point
   float dx = 4.0;
   float dy = 9.5;
   cout <<
      setprecision(3) <<
      "Plus(dx, dy) = " <<
      Plus(dx, dy) << endl;

   // String
   string hello = "Hello, ";
   string world = "world!";
   cout << Plus(hello, world) << endl;

   // Custom type
   X x1(20);
   X x2(22);
   X x3 = Plus(x1, x2);
   cout <<
      "x3.Dump() = " <<
      x3.Dump() << endl;
}
Plus(i, 9) = 13
Plus(dx, dy) = 13.5
Hello, world!
x3.Dump() = 42

Visual Studio 2017 以降: コンパイラは、テンプレートがインスタンス化されるときではなく宣言されるときに、decltype 引数を解析するようになりました。 したがって、引数に decltype 非依存の特殊化が見つかった場合、インスタンス化時に遅延されることはありません。すぐに処理され、その時点で結果のエラーが診断されます。

次の例は、宣言時に発生するこのようなコンパイラ エラーを示しています。

#include <utility>
template <class T, class ReturnT, class... ArgsT> class IsCallable
{
public:
   struct BadType {};
   template <class U>
   static decltype(std::declval<T>()(std::declval<ArgsT>()...)) Test(int); //C2064. Should be declval<U>
   template <class U>
   static BadType Test(...);
   static constexpr bool value = std::is_convertible<decltype(Test<T>(0)), ReturnT>::value;
};

constexpr bool test1 = IsCallable<int(), int>::value;
static_assert(test1, "PASS1");
constexpr bool test2 = !IsCallable<int*, int>::value;
static_assert(test2, "PASS2");

要件

Visual Studio 2010 以降のバージョン。

decltype(auto) には、Visual Studio 2015 以降が必要です。