§20.2.4 [declval]
template <class T>
typename add_rvalue_reference<T>::type declval() noexcept; // as unevaluated operand
为什么在这里使用add_rvalue_reference
?
来自§20.9.7.2 [meta.trans.ref]
上的add_rvalue_reference
:
如果
T
命名对象或函数类型,则成员typedeftype
应命名为T&&
;否则,type
应命名为T
。 [注意:此规则反映了引用折叠的语义(8.3.2)。例如,当类型T
命名类型为T1&
时,类型add_rvalue_reference<T>::type
不是右值引用。 -end note ]
由于add_rvalue_reference
无论如何都要反映引用崩溃,为什么不像以下那样使用T&&
?
template<class T>
T&& declval();
可能出现什么问题?两个版本之间到底有什么区别?
答案 0 :(得分:15)
我不知道这是否是实际原因,但add_rvalue_reference
对void
的行为有所不同。
add_rvalue_reference<void>::type
只是void
。
void&&
是一个错误。
答案 1 :(得分:10)
若干定义取决于declval
为 cv-qualified void
提供合理的结果。一个例子是is_assignable
:
template <class T, class U>
struct is_assignable;
表达式
declval<T>() = declval<U>()
在处理时格式正确 作为一个未经评估的操作数......
意图是“格式良好”是指赋值表达式的良好形成,而不是declval<T>
本身是否格式良好。即我们一次只想担心一件事。
答案 2 :(得分:10)
不同之处在于,如果add_rvalue_reference<>
是对象或函数类型,&&
只会真正添加T
部分。如果T
不是对象或函数类型(例如void
),则您不想添加&&
。
请参阅this example on Ideone This webpage of Boost's implementation解释说:
函数模板
来做得更好declval()
的作用是将类型T
转换为值,而不使用或评估此函数。当且仅当declval<T>()
是左值引用时,该名称应引导读者注意表达式T
是左值的事实,否则为右值。要扩展此函数的域,我们可以通过将其声明更改为template<class T> typename std::add_rvalue_reference<T>::type declval(); // not used
确保我们也可以使用cv
void
作为模板参数。