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

以下代码在libstdc上触发静态断言:
#include <utility>

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

应该是?

这个问题的动机:

以下声明实现proposed by Eric Niebler(显然是编译时优化)

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

如果用户可以合法地观察std :: declval< const void>的类型,将是有问题的.标准中的签名

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

在C 17中导致类型为const void()(或const void()noexcept),而提出的版本导致类型为void()(或void()noexcept).

解决方法

[声明]规定:

If this function is odr-used (3.2),the program is ill-formed.

基本上是这样在功能方面,odr-use意味着,从[basic.def.odr]:

A function whose name appears as a potentially-evaluated expression is odr-used
if it is the unique lookup result or the selected member of a set of overloaded functions (3.4,13.3,13.4),
unless it is a pure virtual function and either its name is not explicitly qualified or the expression forms
a pointer to member (5.3.1).

但也:

An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression
thereof.

和[dcl.type.simple]:

The operand of the decltype specifier is an unevaluated operand (Clause 5).

所以在decltype(std :: declval< const void>)中,std :: declval不是潜在的评估,因此不会被odr使用.既然这是一个程序声明的标准,我们不满足,我觉得libstdc是错误的发出静态断言.

虽然我不认为这是一个libstc的事情.我认为这更是一个static_asserts被触发的问题.声明的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_asserts的正确行为.

相关文章

/** C+⬑ * 默认成员函数 原来C++类中,有6个默认成员函数: 构造函数 析构函数 拷贝...
#pragma once // 1. 设计一个不能被拷贝的类/* 解析:拷贝只会放生在两个场景中:拷贝构造函数以及赋值运...
C类型转换 C语言:显式和隐式类型转换 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译...
//异常的概念/*抛出异常后必须要捕获,否则终止程序(到最外层后会交给main管理,main的行为就是终止) try...
#pragma once /*Smart pointer 智能指针;灵巧指针 智能指针三大件//1.RAII//2.像指针一样使用//3.拷贝问...
目录&lt;future&gt;future模板类成员函数:promise类promise的使用例程:packaged_task模板类例程...