是否允许在std :: declval <T>上使用decltype(函数本身,而不是调用它的结果)?

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).

Bar*_*rry 7

[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.