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

Legitimate to initialize an array in a constexpr constructor?

下面的代码合法吗?

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;

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];
      |         ^~

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

constexpr 上下文 until C++20.

中禁止简单的默认初始化

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

This paper proposes permitting default initialization for trivially default constructible types in constexpr contexts while continuing to disallow the invocation of undefined behavior. In short, so long as uninitialized values are not read from, such states should be permitted in constexpr in both heap and stack allocated scenarios.

自 C++20 起,像您一样保留 v_ "uninitialised" 是合法的。然后您继续为其所有元素分配值,这很棒。

虽然这不能直接回答您的问题,但我认为至少值得一提。您可以简单地使用 in-class 初始化并对数组进行零初始化:

int v_[N]{};

另一种不首先初始化数组的方法是(私下)继承自std::array。奇怪的是,这实际上被 GCC 接受但不被 Clang 接受:

#include <array>

template<int N>
struct foo : private std::array<int, N> {
    constexpr foo() {
        for (auto i = int{}; i < N; ++i) {
            (*this)[i] = i;
        }
    }
};

constexpr foo<5> bar;