问题在于此处broken
本身的初始化。什么是std::initializer_list
?它有什么用?这是一种引用类型(即以某种方式引用了另一个对象),并且由c样式数组支持。这个c样式数组的属性决定了initializer_list是否可以是constexpr变量。对于这些属性,我们可以咨询 [dcl.init.list] 。
5构造了类型为std::initializer_list<E>
的对象
从初始化程序列表中,就好像实现已生成并
实现类型为“ N
const E
的数组的prvalue,其中N
为
初始化程序列表中的元素数。那个的每个要素
数组将使用的相应元素进行复制初始化
初始化程序列表,而std::initializer_list<E>
对象是
构造以引用该数组。 [注意:构造函数或
为副本选择的转换功能应可在
初始化程序列表的上下文。 —尾注]如果缩小
需要转换才能初始化任何元素,程序
格式不正确。 [示例:
struct X {
X(std::initializer_list<double> v);
};
X x{ 1,2,3 };
初始化的方式大致等同于
这个:
const double __a[3] = {double{1},double{2},double{3}};
X x(std::initializer_list<double>(__a,__a+3));
假设实现可以构造一个initializer_list
有一对指针的对象。 —示例]
6数组具有与任何其他临时对象相同的生存期,
除了从数组初始化initializer_list
对象
延长数组的寿命,就像将引用绑定到
一个临时的。 [示例:
typedef std::complex<double> cmplx;
std::vector<cmplx> v1 = { 1,3 };
void f() {
std::vector<cmplx> v2{ 1,3 };
std::initializer_list<int> i3 = { 1,3 };
}
struct A {
std::initializer_list<int> i4;
A() : i4{ 1,3 } {} // ill-formed,would create a dangling reference
};
对于v1
和v2
,initializer_list
对象是
函数调用,因此为{ 1,3 }
创建的数组具有
全表达寿命。对于i3
,initializer_list
对象是
一个变量,因此数组在变量的生存期内一直存在。
对于i4
,initializer_list
对象在
构造函数的ctor-initializer,就像通过将临时数组绑定到
引用成员,因此程序格式不正确([class.base.init])。
— end example] [注意:该实现可自由分配
只读内存中的数组(如果显式数组具有相同的数组)
初始化程序可以这样分配。 —尾注]
因此,此数组就像常量引用所引用的任何其他临时对象一样。这意味着我们实际上可以将您的最小示例减少到更小
constexpr int test_cexpr(int const & x)
{
return x;
}
int main()
{
constexpr int r1 = test_cexpr(0);
constexpr int const &broken = 0;
constexpr int r2 = test_cexpr(broken);
return r1 + r2;
}
这将产生the exact same behavior and error。我们可以直接将0
作为参数传递给constexpr函数,并绑定引用,甚至可以在函数内部引用它。但是,constexpr引用可能未使用0初始化。零不是有效的初始化程序的原因是,它需要实例化临时int
对象。该临时变量不是静态变量,因此不能用于初始化constexpr引用。就这么简单。
相同的理由也适用于您的情况。实现的临时数组不是具有静态存储持续时间的对象,因此它可能不能用于初始化constexpr引用类型。
直接将参数传递给test_cexpr
时起作用的原因是,相应的参数本身不是constexpr变量。这意味着它可以成功绑定。之后,它所绑定的东西必须可以在常量表达式中使用。在此无需赘述:由于在这种情况下,临时项具有完整的表达式生存期(而不是生存期的延长),因此可以在常量表达式中使用。
本文链接:https://www.f2er.com/2836199.html