Законно ініціалізувати масив у конструкторі constexpr?


11

Чи законний наступний код?

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;

Кланг приймає це, але 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_sequences.


1
Gcc10 теж приймає його.
songyuanyao

Ви можете переписати помилку з MSVC?
max66

... і GCC теж.
Євг

1
@songyuanyao - g ++ 10 прийміть його, компілюючи C ++ 20; відмовляється від компіляції C ++ 17 або старшої; Суть здається, що його _vслід ініціалізувати у списку ініціалізації, поки C ++ 17. Можливо, щось змінено в C ++ 20.
max66

2
@Evg Це насправді цікаво, оскільки він може запропонувати Кланг використовувати «усвідомлення» того, що об’єкт тривалості зберігання статичного значення обнуляється, щоб сказати «добре, цей об’єкт, можливо, був ініціалізований за замовчуванням, але читання від його intчлена ніколи не матиме визначеної поведінки ". Цікаво, чи GCC не робить цього сумісним, чи навпаки ...
Гонки легкості на орбіті

Відповіді:


13

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

Причиною, я здогадуюсь, є те, що легко «випадково» прочитати з примітивів, ініціалізованих за замовчуванням, акт, який надає вашій програмі невизначену поведінку, а вирази з невизначеною поведінкою прямо заборонені до використання constexpr( посилання ). Мова була розширена, хоча тепер компілятор повинен перевірити, чи відбувається таке зчитування, і, якщо цього немає, слід прийняти ініціалізацію за замовчуванням. Це трохи більше роботи для компілятора, але (як ви бачили!) Має значні переваги для програміста.

У цьому документі пропонується дозволити ініціалізацію за замовчуванням для тривіально сконструйованих типів за замовчуванням у контекстах constexpr, продовжуючи забороняти виклик невизначеної поведінки. Коротше кажучи, до тих пір, поки не зчитуються неініціалізовані значення, такі стани повинні бути дозволені в constexpr як у купі, так і у стеку, виділених сценаріях.

Оскільки C ++ 20, законно залишати v_"неініціалізованими" такі, як у вас. Потім ви продовжували присвоювати всі його елементи значення, що чудово.


4
@ max66 Мені теж! Все, що я зробив, було відсканувати список змін C ++ 20 у Вікіпедії, знайти щось відповідне constexprта простежити пов'язану пропозицію;)
Гонки легкості в Орбіті

3
Погана частина полягає в тому, що я більше 20 років використовую C ++. Якщо щодня я дізнаюся щось нове ... або я поганий програміст, або C ++ стає занадто складним.
max66

5
@ max66 Це майже напевно останнє. Крім того, той факт, що він корінно змінюється кожні пару років, робить його швидкою рухомою ціллю. Хто може йти в ногу з цим ?! Навіть компілятори не йдуть в ногу з цим.
Гонки легкості на орбіті

@ max66 Цей документ спадає на думку: Пам'ятай про Вазу!
Євг

@Evg О, ух, ця папір мені пройшла повз (IRONY). Пляма на!
Гонки легкості на орбіті
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.