decltype
(C++)
decltype
类型说明符生成指定表达式的类型。 decltype
类型说明符与 auto
关键字一起,主要对编写模板库的开发人员有用。 使用 auto
和 decltype
声明其返回类型取决于其模板自变量类型的函数模板。 或者,使用 auto
和 decltype
声明一个函数模板,用来包装对其他函数的调用,然后返回包装函数的返回类型。
语法
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 int 的 rvalue 引用。 |
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 中,可以结合使用尾随返回类型上的 decltype
类型说明符和 auto
关键字来声明一个函数模板,其返回类型取决于其模板自变量的类型。 例如,考虑下面的代码示例,其中函数模板的返回类型取决于模板参数类型。 在代码示例中,UNKNOWN
占位符指示返回类型无法指定。
template<typename T, typename U>
UNKNOWN func(T&& t, U&& u){ return t + u; };
decltype
类型说明符的引入使开发人员能够获取函数模板返回的表达式的类型。 请使用替代函数声明语法(稍后会展示)、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
模板自变量的类型。 作为最佳编码做法,此代码示例还使用 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 或更高版本。