为什么'declval'用'add_rvalue_reference <T> :: type'指定而不是'T &&'?

Xeo*_*Xeo 20 c++ c++11

§20.2.4 [declval]

template <class T>
typename add_rvalue_reference<T>::type declval() noexcept; // as unevaluated operand
Run Code Online (Sandbox Code Playgroud)

为何add_rvalue_reference在这里使用?

§20.9.7.2 [meta.trans.ref]add_rvalue_reference:

如果T命名对象或函数类型,则成员typedef type应命名T&&; 否则,type应说出名称T.[ 注意:此规则反映了引用折叠的语义(8.3.2).例如,当类型T命名类型时T1&,类型add_rvalue_reference<T>::type不是右值引用.- 尾注 ]

因为add_rvalue_reference无论如何都要反映参考折叠,为什么不使用T&&如下?

template<class T>
T&& declval();
Run Code Online (Sandbox Code Playgroud)

怎么可能出错?两个版本之间到底有什么区别?

Pub*_*bby 17

我不知道这是否是实际原因,但却add_rvalue_reference有不同的行为void.

add_rvalue_reference<void>::type很简单void.

void&& 是一个错误.

  • @Xeo:想象一下涉及`declval <T>`的SFINAE.也许SFINAE"失败"为"T == void"但你希望失败返回false,不会导致编译时错误.让`declval <void>`不会产生编译时错误是有帮助的. (2认同)

How*_*ant 11

几个定义取决于declvalcv-qualified 提供合理的结果void.一个例子是is_assignable:

template <class T, class U>
struct is_assignable;
Run Code Online (Sandbox Code Playgroud)

declval<T>() = declval<U>()当被视为未评估的操作数时,表达式是良好的...

意图是"格式良好"指的是赋值表达式的良好形成,而不是declval<T>它本身是否格式良好.也就是说,我们一次只想担心一件事.


小智 11

不同之处在于,如果是对象或函数类型,add_rvalue_reference<>则只添加&&部分T.如果T不是对象或函数类型(例如void),则不想添加&&.

在Ideone上查看此示例.
Boost实施的这个网页解释说:

函数模板的作用是在不使用或评估此函数的情况declval()下将类型T转换为值.该名称应该引导读者注意这样一个事实:declval<T>()当且仅当T是左值引用时,表达式是左值,否则是右值.要扩展此函数的域,我们可以通过将其声明更改为更好

template<class T>
typename std::add_rvalue_reference<T>::type declval(); // not used
Run Code Online (Sandbox Code Playgroud)

这确保我们也可以使用cv void作为模板参数.