std::declval
是一个编译时实用程序,用于构造表达式以确定其类型。它是这样定义的:
template< class T >
typename std::add_rvalue_reference<T>::type declval() noexcept;
这不是更简单吗?
template< class T >
T declval() noexcept;
引用返回类型的优点是什么?它不应该被称为declref
吗?
我发现的最早的历史例子是n2958,它调用函数value()
,但已经总是返回一个引用。
注意,decltype
的操作数不需要有可访问的析构函数,即它在语义上不作为完整表达式进行检查。
template< typename t >
t declprval() noexcept;
class c { ~ c (); };
decltype ( declprval< c >() ) * p = nullptr; // OK
只有当函数调用本身是decltype
的操作数或是作为decltype
的操作数的逗号运算符的右操作数时(§5.2.2[expr.call]/p11),"在decltype
中返回对象类型的prvalue的函数不引入临时"规则才适用,这意味着在OP中给定declprval
,
template< typename t >
t declprval() noexcept;
class c { ~ c (); };
int f(c &&);
decltype(f(declprval<c>())) i; // error: inaccessible destructor
不编译。更一般地说,返回T
将阻止使用不完整类型的declval
、具有私有析构函数的类型等进行大多数非平凡的使用:
class D;
int f(D &&);
decltype(f(declprval<D>())) i2; // doesn't compile. D must be a complete type
这样做几乎没有什么好处,因为xvalues与prvalues几乎没有区别,除非你在它们上使用decltype
,而且你通常不会直接在declval
的返回值上使用decltype
——你已经知道类型了。
数组不能按值返回,因此即使只是按值返回数组的函数声明也是无效代码。
但是,您可以通过引用返回数组。
decltype()
的目的是让一个表达式充当T
类型的有效值,并将其作为T
s的表达式中的T
。问题是,在C++中,T
类型可能是不可复制的,甚至是不可默认构造的。因此,将T{}
用于此目的是行不通的。
decltype()
所做的是返回对T
的右值引用。右值引用应该对任何类型的T
有效,因此它保证我们从T
的右值引用中获得有效的T
,并且它保证我们可以对任何类型T
具有右值引用。这就是诀窍。
将decltype()
想象为"给我一个类型为T
的有效表达式"。当然,它的用途是用于过载解析、类型确定等;因为它的目的是返回有效表达式(在语法意义上),而不是返回值。这反映在std::declval()
根本没有定义,它只是声明的
如果定义了它,我们又遇到了最初的问题(我们必须为任意类型的T
构造一个值,这是不可能的)。