consitexpr构造函数在GCC编译时进行评估时给出不同的结果

时间:2017-02-14 18:34:30

标签: c++ gcc g++ c++14 constexpr

构造函数使用一个函数来获取引用并按值返回,同时重复修改数据成员:

constexpr int   vv(int   x) {return x;}
constexpr int & rr(int & x) {return x;}
constexpr int   rv(int & x) {return x;}

constexpr struct S {
    int x {0};
    template<typename F> constexpr S(F f) {x = f(x) + 1; x = f(x) + 1;}
} s(rv); // s.x is 1 if function rv is used, 2 otherwise.
static_assert(s.x == 2, "");

只有函数rv在构造函数中使用时会产生意外结果。如果传递vvrr,则s.x按预期为2。

我注意到GCC 5.4.1 ARM上的行为,并且它似乎在支持C ++ 14的所有GCC版本中都是相同的。 Clang在所有情况下都给出了2的预期结果。 GCC和Clang都没有给Wall和Wextra启用任何警告。

此示例是否有效C ++ 14和GCC中的错误?我阅读了标准中对常量表达式的限制列表,看不出任何明显的违反,但我不确定我是否理解所有技术细节。

1 个答案:

答案 0 :(得分:8)

您的代码当然是为了良好的形式。我相信海湾合作委员会会采用经修订的备忘录形式;回到C ++ 11,对象无法在常量表达式中修改,因此缓存函数结果非常好。实际上,从一些Clang错误报告中可以明显看出GCC确实如此,例如, here

  

Clang的constexpr实现不执行缓存。在C ++ 14中,   它甚至不清楚是否可以进行缓存,所以看起来似乎如此   现在浪费时间投资。

他们显然不得不在C ++ 14中引入一些额外的分析,他们犯了一个错误:他们认为任何constexpr对象的子对象在其生命周期内都无法修改。在封闭物体的构造期间,这显然不成立。如果我们使用临时而不是xthe code compiles。如果我们将整个内容放入constexpr函数并删除s {#1}}说明符it works

有趣的是,constexprrr的结果也会被缓存,但返回的相同引用工作正常,并且按值调用可以完全避免这个问题: )

报告为79520