次の方法で共有


decltype (C++)

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

構文

decltype( expression )

パラメーター

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

戻り値

expression パラメーターの型。

解説

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

コンパイラは、expression パラメーターの型を特定するために以下の規則を使用します。

  • expression パラメーターが識別子またはクラス メンバー アクセスの場合、decltype(expression)expression で名前が設定されたエンティティの型になります。 このようなエンティティまたは expression パラメーター名で一連のオーバーロードされた関数に名前を付けると、コンパイラでエラー メッセージが生成されます。

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

  • expression パラメーターが rvalue の場合、decltype(expression)expression の型になります。 expression パラメーターが lvalue の場合、decltype(expression)expression の型への lvalue 参照になります。

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 では、テンプレートの引数の型に応じて戻り値の型が異なる関数テンプレートを宣言するために、auto キーワードと共に、後続の戻り値の型に decltype 型指定子を使用できます。 たとえば、関数テンプレートの戻り値の型がテンプレート引数の型に依存している次のコード例について考えます。 コード例では、UNKNOWN プレースホルダーが、戻り値の型を指定できないことを示しています。

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

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

次のプロトタイプは代替関数宣言の構文について説明します。 const および volatile 修飾子と、throw 例外指定は省略可能です。 function_body プレースホルダーは、関数が何を行うかを指定する複合ステートメントを表します。 コーディングの推奨手順として、decltype ステートメントの expression プレースホルダーは、function_bodyreturn ステートメントによって指定された式に一致する必要があります。

auto function_name ( parametersopt ) constopt volatileopt -> decltype( expression ) noexceptopt { function_body };

次のコード例では、myFunc 関数テンプレートの遅延指定された戻り値の型は、テンプレート引数 tu の型によって決まります。 コーディングの推奨手順として、コード例では、右辺値の参照と完全転送をサポートする 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 以降が必要です。