以下代码触发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的正确行为。