Законно ли инициализировать массив в конструкторе constexpr? - PullRequest
11 голосов
/ 09 января 2020

Допустим ли следующий код?

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 принимает его, но G CC и MSV C отклоняют его.

G CC Ошибка:

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 с.

1 Ответ

13 голосов
/ 09 января 2020

Тривиальная инициализация по умолчанию была запрещена в constexpr контексте до C ++ 20 .

Причина, я предполагаю, заключается в том, что это простое «случайное» чтение из инициализированных по умолчанию примитивов, действие, придающее вашей программе неопределенное поведение, и выражения с неопределенным поведением, прямо запрещено constexpr ( ref ). Язык был расширен, так что теперь компилятор должен проверить, происходит ли такое чтение, и, если это не так, должна быть принята инициализация по умолчанию. Это немного больше работы для компилятора, но (как вы видели!) Имеет существенные преимущества для программиста.

В этой статье предлагается разрешить инициализацию по умолчанию для тривиально конструируемых типов по умолчанию в контекстах constexpr при продолжении запретить вызов неопределенного поведения. Короче говоря, до тех пор, пока неинициализированные значения не читаются, такие состояния должны быть разрешены в constexpr как в сценарии, выделенном для кучи, так и для стека ios.

Начиная с C ++ 20, законно выходить v_ "неинициализирован", как у вас. Затем вы перешли к присвоению значений всех его элементов, и это здорово.

...