Подтвердить что ты не робот

С++ 1y/С++ 14: Назначение объекта за пределами его жизни не допускается в постоянном выражении?

Является ли следующая программа С++ 14/С++ 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;
}

Странный ствол (при написании) дает:

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

Что означает "назначение объекту за пределами его жизни"? Время жизни x и его подобъектов включает функцию, так что это о чем?

4b9b3361

Ответ 1

Программа плохо сформирована, потому что вы не инициализируете x, если вы измените определение на:

literal_array<T, n+m> x = {{0}};

clang больше не жалуется и компилируется без ошибок. Другим решением было бы создание консректоров constexpr.

Мы можем найти это в разделе черновик проекта 7.1.5 Параграф 3 описания constexpr, который гласит:

Определение функции constexpr должно удовлетворять следующему Ограничения:

и включает следующую марку:

его тело функции должно быть = delete, = default, или составной оператор, который не содержит

который содержит эту пулю (акцент мой):

определение переменной нелитерального типа или статического или потока время хранения или для которого не выполняется инициализация.

а затем мы имеем следующий пример:

constexpr int uninit() {
  int a; // error: variable is uninitialized
  return a;
}

Жалоба о времени жизни x, похоже, не основана на черновом проекте. Правильная причина, насколько я могу судить, должна быть чем-то вроде object is not initialized.

Соответствующая цитата из проекта стандарта на время жизни объекта будет секцией 3.8 Object lifetime paragraph 1, которая гласит:

Время жизни объекта - это свойство времени выполнения объекта. считается, что объект имеет нетривиальную инициализацию, если он имеет класс или совокупный тип, и он или один из его членов инициализируется конструктор, отличный от тривиального конструктора по умолчанию. [ Заметка: инициализация тривиальным конструктором copy/move является нетривиальной инициализация. - end note] Наступает время жизни объекта типа Tкогда:

  • получено хранилище с правильным выравниванием и размером для типа T и
  • Если объект имеет нетривиальную инициализацию, его инициализация завершена.

На всякий случай, когда я отсутствовал, я также проверил с помощью std:: is_trivial:

std::cout <<  std::boolalpha << std::is_trivial<literal_array<int, 3>>::value << std::endl ;

и результат, как ожидалось в true,

Обновить

Я написал отчет об ошибках для этого, и ответ включает в себя следующее утверждение:

[...] Проблема в том, что мы еще не реализуем подразумеваемое правило, что такую ​​функцию нельзя вызывать в постоянном выражении.