decltype
型指定子は、指定された式の型を生成します。 decltype
型指定子は、auto
キーワードと共に使用され、主にテンプレート ライブラリを記述する開発者にとって有益です。 auto
と decltype
を使用して、テンプレートの引数の型に応じた戻り値の型を持つ関数テンプレートを宣言します。 または、auto
と decltype
を使用して、別の関数の呼び出しをラップした関数テンプレートを宣言し、ラップされた関数の戻り値の型を返します。
構文
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& |
内側のかっこは、ステートメントをメンバー アクセスではなく式として評価します。 そして、a は const ポインターとして宣言されているため、型は 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_body
で return
ステートメントによって指定された式に一致する必要があります。
auto
function_name
(
parameters
opt )
const
opt volatile
opt ->
decltype(
expression
)
noexcept
opt {
function_body
};
次のコード例では、myFunc
関数テンプレートの遅延指定された戻り値の型は、テンプレート引数 t
と u
の型によって決まります。 コーディングの推奨手順として、コード例では、右辺値の参照と完全転送をサポートする 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 以降が必要です。