关于常量表达式的困惑 [英] Confusion about constant expressions
问题描述
这是此主题的某种后续版本,并涉及了它的一小部分。与上一个主题一样,让我们考虑一下,我们的编译器对 std :: initializer_list
和<$ c $具有 constexpr
函数c> std :: array 。现在,让我们直接说清楚。
This is some kind of follow-up for this topic and deals about a little part of it. As with the previous topic, let's consider that our compiler has constexpr
functions for std::initializer_list
and std::array
. Now, let's go straight to the point.
#include <array>
#include <initializer_list>
int main()
{
constexpr std::array<int, 3> a = {{ 1, 2, 3 }};
constexpr int a0 = a[0];
constexpr int a1 = a[1];
constexpr int a2 = a[2];
constexpr std::initializer_list<int> b = { a0, a1, a2 };
return 0;
}
这不是:
#include <array>
#include <initializer_list>
int main()
{
constexpr std::array<int, 3> a = {{ 1, 2, 3 }};
constexpr std::initializer_list<int> b = { a[0], a[1], a[2] };
return 0;
}
由于以下错误而崩溃:
error: 'const std::initializer_list<int>{((const int*)(&<anonymous>)), 3u}' is not a constant expression
即使我读过一些有关 constexpr
的论文同时使用常量表达式,这种行为对我来说仍然没有任何意义。为什么第一个示例被视为有效的常量表达式而不是第二个示例?我欢迎任何解释,以便以后我可以安息。
Even though I read some papers about constexpr
and constant expressions meanwhile, this behaviour still does not make any sense for me. How come the first example is considered a valid constant expression and not the second one? I would welcome any explanation so that I can rest in peace afterwards.
注意:我会立即加以纠正,Clang将无法编译第一个代码段,因为它没有实现计划用于C ++ 14的 constexpr
库添加。我使用的是GCC 4.7。
NOTE: I will precise it right away, Clang will not be able to compile the first snippet since it does not implement the constexpr
library additions that are planned for C++14. I used GCC 4.7.
编辑:好的,这是一个展示拒绝和不拒绝的大例子:
Ok, here comes the big example to show what is rejected and what is not:
#include <array>
#include <initializer_list>
constexpr int foo = 42;
constexpr int bar() { return foo; }
struct eggs { int a, b; };
int main()
{
constexpr std::array<int, 3> a = {{ 1, 2, 3 }};
constexpr int a0 = a[0];
constexpr int a1 = a[1];
constexpr int a2 = a[2];
// From Xeo and Andy tests
constexpr std::array<int, 1> a = { bar() }; // OK
constexpr std::array<int, 3> b = {{ a[0], a[1], a[2] }}; // OK
std::initializer_list<int> b = { a[0], a[1], a[2] }; // OK
constexpr std::initializer_list<int> b = { a0, a1, a2 }; // OK
constexpr std::initializer_list<int> b = { foo }; // OK
constexpr std::initializer_list<int> c = { bar() }; // ERROR
constexpr std::initializer_list<int> b = { a[0], a[1], a[2] }; // ERROR
// From Matheus Izvekov and Daniel Krügler
constexpr eggs good = { 1, 2 }; // OK
constexpr std::initializer_list<eggs> bad = { { 1, 2 }, { 3, 4 } }; // ERROR
constexpr std::initializer_list<eggs> bad2 = { good, good }; // ERROR
return 0;
}
推荐答案
tl / dr:初始化程序是非恒定的,因为它指的是不同的
tl/dr: The initializer is non-constant because it refers to a different temporary each time the function is evaluated.
声明:
constexpr std::initializer_list<int> b = { a0, a1, a2 };
创建类型为 const int [3] $ c的临时数组$ c>(C ++ 11 [dcl.init.list] p5 ),然后将
std :: initializer_list< int>
对象绑定到就像通过绑定对它的引用(C ++ 11 [dcl.init.list] p6 )一样。
creates a temporary array of type const int [3]
(C++11 [dcl.init.list]p5), then binds the std::initializer_list<int>
object to that temporary as if by binding a reference to it (C++11 [dcl.init.list]p6).
现在,通过C ++ 11 [expr.const] p4 ,
Now, by C++11 [expr.const]p4,
用于数组或类类型的文字常量表达式,每个子对象应已通过常量表达式初始化。 [...]地址常量表达式 [...]的计算结果为具有静态存储持续时间的对象的地址。
For a literal constant expression of array or class type, each subobject [...] shall have been initialized by a constant expression. [...] An address constant expression [...] evaluates to the address of an object with static storage duration.
由于 b
具有自动存储持续时间,当 std :: initializer_list< int>
对象绑定到 const int [3]
临时对象,该临时对象还具有自动存储期限,因此初始化 b
不是常量表达式,因为它指向没有静态存储持续时间的对象的地址。因此 b
的声明格式不正确。
Since b
has automatic storage duration, when the std::initializer_list<int>
object binds to the const int [3]
temporary, the temporary is also given automatic storage duration, so the initialization of b
is not a constant expression because it refers to the address of an object that does not have static storage duration. So the declaration of b
is ill-formed.
为什么GCC接受某些 constexpr
std :: initializer_list
对象
Why GCC accepts some of the constexpr
std::initializer_list
objects
在初始化器很简单的地方,GCC(和Clang)将阵列提升为全局存储,而不是每次都创建一个新的临时存储。但是,在GCC中,此实现技术会泄漏到语言语义中-GCC将数组视为具有静态存储持续时间,并接受初始化(作为对C ++ 11规则的意外或故意扩展)。
In cases where the initializer is suitably trivial, GCC (and Clang) promote the array to global storage rather than creating a new temporary each time around. However, in GCC, this implementation technique leaks through to the language semantics -- GCC treats the array as having static storage duration, and accepts the initialization (as either an accidental or deliberate extension to the C++11 rules).
解决方法(仅Clang)
您可以通过给出 std :: initializer_list< int>
对象的静态存储持续时间:
You can make your examples valid by giving the std::initializer_list<int>
objects static storage duration:
static constexpr std::initializer_list<int> b = { a0, a1, a2 };
这反过来为数组提供了静态存储持续时间,这使初始化成为一个常量表达式。
This in turn gives static storage duration to the array temporary, which makes the initialization be a constant expression.
使用Clang和libc ++(将 constexpr
添加到libc ++的< array>
和< initializer_list>
),添加 static
的这种调整足以满足您的示例将被接受。
Using Clang and libc++ (with constexpr
added to libc++'s <array>
and <initializer_list>
in the appropriate places), this tweak of adding static
is sufficient for your examples to be accepted.
使用GCC,示例仍被拒绝,并带有以下诊断信息:
Using GCC, the examples are still rejected, with diagnostics such as:
<stdin>:21:61: error: ‘const std::initializer_list<int>{((const int*)(& _ZGRZ4mainE1c0)), 1u}’ is not a constant expression
此处, _ZGRZ4mainE1c0
是错误的名称生命周期扩展的数组临时(具有静态存储持续时间),我们可以看到GCC隐式调用了(private) initializer_list< int>(const int *,size_t)
构造函数。我不确定为什么海湾合作委员会仍会拒绝这样做。
Here, _ZGRZ4mainE1c0
is the mangled name of the lifetime-extended array temporary (with static storage duration), and we can see that GCC is implicitly calling the (private) initializer_list<int>(const int*, size_t)
constructor. I am not sure why GCC is still rejecting this.
这篇关于关于常量表达式的困惑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!