在cppref中,它提供了常量初始化的语法:
static T & ref = constexpr;
static T object = constexpr;
这是我的两个问题:
如果没有T &
的左值引用const
如何绑定到constexptr
,这是恒定且不可修改的?
我尝试提供一些示例但失败了:
static int& ref = 6; //error, need a `const`
constexpr int a = 6; static int& ref = a; //error, need a `const`
常量初始化对象是const
/ static
的必要性吗?在the standard中它说:
如果具有静态或线程存储持续时间的变量或临时对象由实体的常量初始化程序初始化,则执行常量初始化。
此处标准未指定obj为const-qualified
/ static-qualified
。
答案 0 :(得分:6)
试图说的是
static int a;
static int & ref = a;
static_assert(&a == &ref, "");
没关系。初始化是一种常量初始化的形式,因为a
在被评估为左值时是一个常量表达式(但仅作为左值!),因此,&a == &ref
是一个常量表达式,用于求值到true
。
将此与
进行比较void f() {
int a;
static int & ref = a;
static_assert(&a == &ref, "");
}
这是无效的。虽然ref
的初始化在技术上是有效的,但只要函数返回它就会成为悬空引用。下次输入该函数时,将创建一个新的int a
对象。因此,&a == &ref
无法保证评估为true
。它不是一个常量表达式,如果进行求值,它将具有未定义的行为。
答案 1 :(得分:5)
混淆是由于命名:常量初始化 [basic.start.static]/2和常量表达式 {{3}中的术语常量 }表示在编译时可评估,无需编译器的英雄努力(1)。这与常量对象的概念不同,这意味着一旦定义,对象的值就不会改变。
为了说明编译时的评估限制,让我们看看这段代码的汇编:
//case 0
int i0 = 5;
int j0 = i0;//no compil-time initialized
//case 1
const int i1=5;
int j1=i1; //compil-time initialized
//case 2
extern const int i2=5;
int j2=i2; //compile-time initialized
//case 3
extern const int i3;
int j3=i3; //no compil-time initialization
//case 4
extern const int i4;
int j4=i4; //no compil-time initialization
const int i4=5;
通过gcc 7.3生成程序集:
_GLOBAL__sub_I_example.cpp: # @_GLOBAL__sub_I_example.cpp
mov eax, dword ptr [rip + i0]
mov dword ptr [rip + j0], eax
mov eax, dword ptr [rip + i3]
mov dword ptr [rip + j3], eax
mov dword ptr [rip + j4], 5
ret
发生了什么:
(1)原则是语言不能太复杂而无法编译。我只是回收了模板参数演绎标准的措辞,其中英雄努力一词出现了。同样的原则用于定义什么是常量表达式。
答案 2 :(得分:2)
"持续初始化"表示初始值设定项是一个常量表达式。表达式和变量都不需要是const限定的。
文件范围内的 int x = 6;
是常量初始化。
参考:C ++ 17 [basic.start.static] / 2:
如果具有静态或线程存储持续时间的变量或临时对象由实体的常量初始化程序初始化,则执行常量初始化。
答案 3 :(得分:1)
在您引用的页面中,您可以阅读
设置静态常量的初始值
我将注意力集中在常数
上所以T
必须是常量类型。
所以int const
没问题; constexpr int
没问题,因为constexpr
暗示const
;没有int
{或没有const
暗示constexpr
)的const
是错误的。