C++ 1y/C++ 14:在常量表达式中不允许在其生命周期之外赋值给对象吗?

And*_*zos 16 c++ language-lawyer constexpr c++14

根据当前的草案,以下C++ 14/C++ 1y程序是否格式错误?

#include <cstddef>

template<typename T, size_t n>
struct literal_array
{
    T data[n];
};

template<typename T, size_t n, size_t m>
constexpr literal_array<T, n+m> operator+(literal_array<T, n> a,
                                          literal_array<T, m> b)
{
    literal_array<T, n+m> x;

    for (size_t i = 0; i < n; i++)
        x.data[i] = a.data[i];

    for (size_t i = 0; i < m; i++)
        x.data[n+i] = b.data[i];

    return x;
}

int main()
{
    constexpr literal_array<int, 3> a = { 1, 2, 3 };
    constexpr literal_array<int, 2> b = { 4, 5 };

    constexpr auto c = a + b;
}
Run Code Online (Sandbox Code Playgroud)

Clang trunk(在撰写本文时)给出:

error: constexpr variable 'c' must be initialized by a constant expression
        constexpr auto c = a + b;
                       ^   ~~~~~
assignment to object outside its lifetime is not allowed in a constant expression
                x.data[i] = a.data[i];
                          ^
in call to 'operator+({{1, 2, 3}}, {{4, 5}})'
        constexpr auto c = a + b;
                           ^
Run Code Online (Sandbox Code Playgroud)

什么意思"在生命之外分配对象"?x及其子对象的生命周期包含了函数,那么它的含义是什么?

Sha*_*our 10

如果您将定义更改为以下内容,则该程序格式不正确x,因为您没有初始化:

literal_array<T, n+m> x = {{0}};
Run Code Online (Sandbox Code Playgroud)

clang不再抱怨,它编译没有错误.另一个解决方案是创建constexpr consrtuctors.

我们可以在标准草案章节草案中找到这一点7.1.5 :constexpr说明符3段说:

constexpr函数的定义应满足以下约束:

并包括以下项目:

它的函数体应该是= delete,= default或者是不包含的 复合语句

其中包含这个子弹(强调我的):

非文字类型或静态或线程存储持续时间的变量的定义,或者不执行初始化的定义.

稍后我们有以下示例:

constexpr int uninit() {
  int a; // error: variable is uninitialized
  return a;
}
Run Code Online (Sandbox Code Playgroud)

关于生命周期的抱怨x似乎并未在草案标准中找到.据我所知,正确的理由应该是符合的object is not initialized.

关于对象生命周期的标准草案的相关引用将是3.8 对象生命周期段落1,其中说:

对象的生命周期是对象的运行时属性.如果一个对象属于类或聚合类型,并且它或其成员之一由除了普通默认构造函数之外的构造函数初始化,则称该对象具有非平凡的初始化.[ 注意: 通过简单的复制/移动构造函数进行初始化是非平凡的初始化.- 结尾注释 ]类型对象的生命周期T从以下开始:

  • 获得具有适当对齐和类型大小的存储T,并且
  • 如果对象具有非平凡的初始化,则其初始化完成.

为了防止我遗漏了一些东西,我还使用std :: is_trivial进行了检查:

std::cout <<  std::boolalpha << std::is_trivial<literal_array<int, 3>>::value << std::endl ;
Run Code Online (Sandbox Code Playgroud)

和预期的结果true,.

更新

我为此提交了一份错误报告,答复中包含以下声明:

[...]问题是我们还没有实现隐含的规则,即不能在常量表达式中调用这样的函数.

  • 您的分析是正确的 - Clang使用该诊断,因为该对象未初始化.在`constexpr`函数中不应该使用未初始化的对象,因此我们没有'未初始化'诊断.问题是我们不应该允许调用`operator +`,因为它不会初始化`x`.(另请参阅我在你的clang bug报告中留下的更长的解释.) (3认同)