decltype (C++)

decltype 类型说明符生成指定表达式的类型。 decltype 类型说明符与 auto 关键字一起,主要对编写模板库的开发人员有用。 使用 autodecltype 声明其返回类型取决于其模板自变量类型的函数模板。 或者,使用 autodecltype 声明一个函数模板,用来包装对其他函数的调用,然后返回包装函数的返回类型。

语法

decltype( expression )

参数

expression
一个表达式。 有关详细信息,请参阅表达式

返回值

expression 参数的类型。

备注

decltype 类型说明符在 Visual Studio 2010 或更高版本中受支持,可与本机或托管代码一起使用。 Visual Studio 2015 及更高版本支持 decltype(auto) (C++14)。

编译器使用下列规则来确定 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();

接下来,检查由下表中四个 decltype 语句返回的类型。

语句 类型 说明
decltype(fx()); const int&& const intrvalue 引用
decltype(var); int 变量 var 的类型。
decltype(a->x); double 成员访问的类型。
decltype((a->x)); const double& 内部括号导致语句作为表达式而不是成员访问计算。 由于 a 声明为 const 指针,因此类型是对 const double 的引用。

decltypeauto

在 C++14 中,可以使用不带尾随返回类型的 decltype(auto) 来声明一个函数模板,其返回类型取决于其模板自变量的类型。

在 C++11 中,可以结合使用尾随返回类型上的 decltype 类型说明符和 auto 关键字来声明一个函数模板,其返回类型取决于其模板自变量的类型。 例如,考虑下面的代码示例,其中函数模板的返回类型取决于模板参数类型。 在代码示例中,UNKNOWN 占位符指示返回类型无法指定。

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

decltype 类型说明符的引入使开发人员能够获取函数模板返回的表达式的类型。 请使用替代函数声明语法(稍后会展示)auto 关键字和 decltype 类型说明符来声明后指定返回类型。 后指定返回类型是在对声明进行编译而不是编码时确定的。

以下原型阐述一个替代函数声明的语法。 constvolatile 限定符以及 throw 异常规范都是可选的。 function_body 占位符表示指定函数作用的复合语句。 作为最佳编码做法,decltype 语句中的 expression 占位符应与 function_bodyreturn 语句(如果有)指定的表达式匹配。

autofunction_name(parametersopt)constoptvolatileopt->decltype(expression)noexceptopt{function_body};

在下面的代码示例中,myFunc 函数模板的后指定返回类型取决于 tu 模板自变量的类型。 作为最佳编码做法,此代码示例还使用 rvalue 引用和 forward 函数模板来支持完美转移。 有关详细信息,请参阅 Rvalue 引用声明符:&&

//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+ 重载处理它的两个操作数。 因此,对 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 或更高版本。