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

Is it allowed to use decltype on std::declval<T> (the function itself, not the result of calling it)?

本文关键字:函数 结果 调用 std eclval lt 是否 decltype gt      更新时间:2023-10-16

以下代码触发libstdc++上的静态断言:

#include <utility>
using t = decltype(std::declval<const void>);

应该吗?


这个问题的动机:

下面由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));

如果用户能够合法地观察CCD_ 2的类型。标准中的签名

template <class T>
add_rvalue_reference_t<T> declval() noexcept;

导致类型const void ()(或C++17中的const void () noexcept),而所提出的版本导致类型void ()(或void () noexcept)。

[delval]规定:

如果使用此函数(3.2),则程序格式错误。

基本上就是这样。在涉及函数的地方,odr使用的方法是从〔basic.def.odr〕:

名称显示为潜在求值表达式的函数是odr使用的如果它是唯一的查找结果或一组重载函数(3.4、13.3、13.4)的所选成员,除非它是一个纯虚拟函数,并且它的名称没有明确限定或者表达式形式指向成员(5.3.1)的指针。

但也包括:

表达式可能被求值,除非它是未求值的操作数(第5条)或子表达式其中。

和[dcl.type.simple]:

decltype说明符的操作数是未赋值的操作数(第5条)。

因此,在decltype(std::declval<const void>)中,std::declval没有被潜在地评估,因此它没有被odr使用。由于这是declval上程序格式错误的一个标准,而且我们不符合它,所以我认为libstdc++发出静态断言是错误的。


虽然我不认为这是一个libstc++的东西。我认为更多的是static_assert何时被触发的问题。declval的libstdc++实现是:

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();
} 

在这种情况下,gcc和clang都会触发static_assert(但显然不是decltype(std::declval<const void>()),尽管我们在这两种情况下都处于未评估的上下文中。我怀疑这是一个错误,但在标准中可能只是没有明确说明触发static_assert s的正确行为。