关于常量表达式的困惑 [英] Confusion about constant expressions

查看:107
本文介绍了关于常量表达式的困惑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是此主题的某种后续版本,并涉及了它的一小部分。与上一个主题一样,让我们​​考虑一下,我们的编译器对 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 ++ 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆