在 constexpr 构造函数中初始化数组是否合法?

Yon*_* Wu 13 c++ compile-time-constant template-meta-programming constexpr

下面的代码合法吗?

template <int N>
class foo {
public:
    constexpr foo()
    {
        for (int i = 0; i < N; ++i) {
            v_[i] = i;
        }
    }

private:
    int v_[N];
};

constexpr foo<5> bar;
Run Code Online (Sandbox Code Playgroud)

Clang 接受它,但 GCC 和 MSVC 拒绝它。

GCC 的错误是:

main.cpp:15:18: error: 'constexpr foo<N>::foo() [with int N = 5]' called in a constant expression
   15 | constexpr foo<5> bar;
      |                  ^~~
main.cpp:4:15: note: 'constexpr foo<N>::foo() [with int N = 5]' is not usable as a 'constexpr' function because:
    4 |     constexpr foo()
      |               ^~~
main.cpp:4:15: error: member 'foo<5>::v_' must be initialized by mem-initializer in 'constexpr' constructor
main.cpp:12:9: note: declared here
   12 |     int v_[N];
      |         ^~
Run Code Online (Sandbox Code Playgroud)

如果这种代码没问题,我可以减少很多index_sequences 的使用。

Lig*_*ica 17

C++20 之前constexpr上下文中禁止平凡的默认初始化。

我猜,原因是很容易“意外地”从默认初始化的原语中读取,这种行为会给您的程序带来未定义的行为,并且直接禁止具有未定义行为的表达式constexprref)。该语言已被扩展,因此现在编译器必须检查是否发生了这样的读取,如果没有,则应接受默认初始化。编译器需要做更多的工作,但是(如您所见!)对程序员有很大的好处。

本文建议允许在 constexpr 上下文中为平凡的默认可构造类型进行默认初始化,同时继续禁止调用未定义的行为。简而言之,只要不读取未初始化的值,在堆和堆栈分配方案中的 constexpr 中都应该允许此类状态。

从 C++20 开始,v_像你一样离开“未初始化”是合法的。然后你继续分配它的所有元素值,这很棒。

  • @max66 几乎可以肯定是后者。此外,它每隔几年就会发生根本性的变化,这一事实使其成为一个快速移动的目标。谁能跟得上?!即使编译器也跟不上。 (5认同)
  • @max66 我也是!我所做的就是扫描 Wikipedia 上的 C++20 更改列表,找到与“constexpr”相关的内容,然后浏览链接的提案;) (4认同)
  • 不好的是我使用 C++ 已有 20 多年了。如果我每天都学到新东西……或者我是一个糟糕的程序员或者 C++ 变得太复杂了。 (3认同)