N4527 7.1.5 [dcl.constexpr] p9
对象声明中使用的constexpr说明符将对象声明为const。这样的对象应具有文字类型并应初始化。如果它是由构造函数调用初始化的,那么该调用应该是一个常量 表达式(5.20)。 否则,或者如果在引用声明中使用constexpr说明符,则其初始值设定项中出现的每个完整表达式都应为常量表达式。
5.20 [expr.const] P5
常量表达式是glvalue核心常量表达式,其值指的是一个实体 允许的常量表达式结果(如下定义),或者是一个prvalue核心常量表达式 value是一个对象,对于该对象及其子对象:
- 引用类型的每个非静态数据成员引用一个实体,该实体是常量表达式的允许结果,并且
- 如果对象或子对象是指针类型,则它包含具有静态存储持续时间的对象的地址,超过此类对象末尾的地址(5.7),函数的地址或空指针值
实体是常量表达式的允许结果,如果它是具有静态存储持续时间的对象,该对象不是临时对象,或者是其值满足上述约束的临时对象,或者它是 功能
void foo(){
constexpr const int &a = 1;//error
constexpr static const int &b = 1;//ok in gcc 5.1.0 and clang 3.8.0
}
问题:为什么constexpr const int &a = 1;
在块范围内失败?
答案 0 :(得分:4)
cwg defect report 2005: Incorrect constexpr reference initialization requirements中说明了这一点(强调我的):
考虑一个例子:
constexpr int f() { return 5; } // function must be constexpr constexpr int && q = f(); // but result is not constant constexpr int const & r = 2; // temporary is still not constant int main() { q = 11; // OK const_cast< int & >( r ) = 3; // OK (temporary object is not ROMable) constexpr int && z = 7; // Error? Temporary does not have static storage duration? }
constexpr引用必须通过常量表达式初始化 (7.1.5 [dcl.constexpr]第9段),但它可以指可修改的 临时对象。这样的临时保证是静态的 初始化,但它不是ROMable。
使用左值表达式初始化的非const constexpr引用 很有用,因为它表明了底层存储的 引用可能是静态初始化的,或者没有底层存储 完全是必需的。
当初始化程序是临时的时,查找其地址是微不足道的。 没有理由声明其计算任何意图 地址。另一方面,提供初始值,即 虽然它从未被处理过,但也需要不断表达 作为常数。
本地constexpr参考的情况更糟。初始化程序 在执行声明时生成临时值。暂时的 是一个本地作用域,唯一的对象。这使得constexpr 没有意义,因为虽然地址计算是微不足道的,但它 仍然必须动态完成。
C ++ 11 constexpr引用需要通过引用进行初始化 常量表达式,必须“用静态指定一个对象 存储持续时间或函数“(C ++ 11 5.20 [expr.const]第3段)。 由引用授予的临时自动存储持续时间 没有这个要求。
C ++ 14删除了引用常量表达式和静态存储 要求,明确地使用明确定义的程序 击败了constexpr说明者。 (GCC和Clang目前提供 C ++ 11诊断。)
建议解决方案:临时绑定到constexpr引用 应该是constexpr,意味着const限定类型。禁止 将constexpr引用绑定到临时,除非两者都有静态 储存期限。 (在本地范围内,静态说明符修复了 问题很好。)
答案是5.20
第4段已禁止这样做:
5.20 [expr.const]第4段已涵盖此问题,其中包括分析中的转换和临时工具。