问题描述
问题出在这里
int main()
{
constexpr std::initializer_list<int> my_ints {1,2,3};
}
我正在尝试使用g ++(x86_64-posix-seh-rev0,版本8.1.0)编译以上代码。但是VS Code提出以下警告:
“表达式必须具有恒定值-寿命有限的临时引用或指针”
int main()
{
std::initializer_list<int> my_ints {1,3};
}
错误消失了,所以声明constexpr initializer_list
似乎有问题。
根据此参考文献(https://en.cppreference.com/w/cpp/utility/initializer_list/initializer_list),最好声明一个constexpr initializer_list
。
可以更深入地了解constexpr
和initializer_list
的工作原理的人吗?
解决方法
在cppreference处有一个导致此问题的原因的线索:
std :: initializer_list类型的对象是轻量级代理对象,可提供对const T类型的对象数组的访问。
和:
初始化器列表可以实现为一对指针或指针和长度。
换句话说,当您在本地范围内执行此操作时:
std::initializer_list<int> my_ints {1,2,3};
编译器必须在堆栈上分配(并初始化){1,3}
,因此不能为constexpr
。
幸运的是,有一个简单的解决方法。只要做:
static constexpr std::initializer_list<int> my_ints {1,3};
现在底层数组是在编译时分配的,因此可以为constexpr
。
生成的代码的差异可以在Godbolt上看到。
编辑:OP询问(实际上)基于堆栈的变量是否曾经是constexpr
?好吧,如果可以在编译时评估该对象的所有成员,答案是肯定的。
因此,回到原始示例,当您编写:
std::initializer_list<int> my_ints {1,3};
std::initializer_list
通常由指向基础数组的指针和长度组成,因此,这里的编译器必须将1
,2
和3
推入在运行时堆栈,然后将指向该“数组”的指针和长度放入my_ints
中,因此my_ints
不能为constexpr
,因为在编译时未知该数组的地址。
OTOH,类似:
constexpr int i = 42;
很好,因为(显然)在编译时就知道42,这是编译器关心的所有事情。
有趣的是:
constexpr char s [] = "abcde";
也可以。如果您查看code generated,则编译器会在静态存储中分配“ abcde”,然后在运行时将其复制到s
。为什么在std::initializer_list
上它没有做类似的事情(至少对我来说是个谜)。
最后,为什么声明my_ints
static
可以解决问题?好了,现在编译器可以在编译时分配1,3
并将其初始化,并且现在 的“数组”位于编译时已知的地址,因此my_ints
可以是{{ 1}}。