T.C*_*.C. 12 c++ language-lawyer c++11
以下代码在libstdc ++上触发静态断言:
#include <utility>
using t = decltype(std::declval<const void>);
Run Code Online (Sandbox Code Playgroud)
应该是?
这个问题的动机:
Eric Niebler提出的以下declval实现(显然是编译时优化)
template<typename _Tp, typename _Up = _Tp&&>
_Up __declval(int);
template<typename _Tp>
_Tp __declval(long);
template<typename _Tp>
auto declval() noexcept -> decltype(__declval<_Tp>(0));
Run Code Online (Sandbox Code Playgroud)
如果用户可以合法地观察其类型,那将是有问题的std::declval<const void>.标准中的签名
template <class T>
add_rvalue_reference_t<T> declval() noexcept;
Run Code Online (Sandbox Code Playgroud)
结果类型const void ()(或const void () noexcept在C++ 17中),而建议的版本导致类型void ()(或void () noexcept).
[declval]规定:
如果此功能使用了odr-used(3.2),则程序格式错误.
基本上就是这样.在涉及函数的地方,odr-use意味着来自[basic.def.odr]:
如果一个函数的名称显示为可能被评估的表达式,则它是唯一的查找结果或一组重载函数(3.4,13.3,13.4)的选定成员,除非它是一个纯虚函数,否则它将被使用. name未显式限定,或者表达式形成指向成员的指针(5.3.1).
但是也:
除非是未评估的操作数(第5条)或其子表达式,否则可能会对表达式进行求值.
并且[dcl.type.simple]:
说明符的操作数
decltype是未评估的操作数(第5条).
因此decltype(std::declval<const void>),std::declval没有潜在的评估,因此它没有使用.由于这是declval程序错误形成的一个标准,我们不满足它,我认为libstdc ++发出静态断言是错误的.
虽然我不认为这是libstc ++的事情.我认为这更像是一个什么时候static_assert被触发的问题.libstdc ++实现declval是:
template<typename _Tp>
struct __declval_protector
{
static const bool __stop = false;
static typename add_rvalue_reference<_Tp>::type __delegate();
};
template<typename _Tp>
inline typename add_rvalue_reference<_Tp>::type
declval() noexcept
{
static_assert(__declval_protector<_Tp>::__stop,
"declval() must not be used!");
return __declval_protector<_Tp>::__delegate();
}
Run Code Online (Sandbox Code Playgroud)
static_assert在这种情况下,gcc和clang都会触发(但显然没有decltype(std::declval<const void>()),即使我们在两种情况下都处于未评估的上下文中.我怀疑这是一个错误,但在标准中可能只是未明确说明正确的行为是什么触发static_asserts.