Должен ли явно заданный по умолчанию constexpr ctor разрешать инициализацию без constexpr

Я только что наткнулся на следующие различия между GCC и Clang, касающиеся явно заданного по умолчанию constexpr ctor и некоторого наследования...

template <typename T>
struct A {
  constexpr A() = default;
  T v;
};

struct B : A<int> {
  constexpr B() = default;
};

GCC немедленно отклоняет код, в то время как Clang позволяет создавать версии без constexpr обоих типов. Я предполагаю, что Clang, вероятно, прав, но я не уверен на 100%...

инициализируйте A::v и gcc будет счастлив.

Swordfish 10.05.2019 23:58
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
1
267
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема сводится к: это конструктор constexpr, который инициализирует по умолчанию некоторый нестатический член данных встроенного типа допустим, если не используется?


тл;др:

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

  • Для конструктора шаблонов да, допустимо иметь некоторые (но не все, диагностика не требуется) инстанцированные специализации шаблонов для которых созданный конструктор не соответствует требованиям конструктора constexpr.

В этом случае GCC прав, а Clang нет.


GCC выдает следующее сообщение об ошибке, которое очень информативно:

prog.cc:8:13: error: explicitly defaulted function 'constexpr B::B()' cannot be declared as 'constexpr' because the implicit declaration is not 'constexpr':
    8 |   constexpr B() = default;
      |             ^
prog.cc:3:13: note: defaulted constructor calls non-'constexpr' 'A<T>::A() [with T = int]'
    3 |   constexpr A() = default;
      |             ^
prog.cc:3:13: note: 'A<T>::A() [with T = int]' is not usable as a 'constexpr' function because:
prog.cc:4:5: note: defaulted default constructor does not initialize 'int A<int>::v'
    4 |   T v;
      |     ^

live demo

Обратите внимание, что ошибка возникает в конструкторе B, вместо A, чей конструктор просто «непригоден для использования в качестве функции constexpr потому что [] конструктор по умолчанию по умолчанию не инициализирует int A<int>::v."


Согласно [dcl.constexpr]/4:

The definition of a constexpr constructor shall satisfy the following requirements:

  • the class shall not have any virtual base classes;
  • each of the parameter types shall be a literal type.

In addition, either its function-body shall be = delete, or it shall satisfy the following requirements:

  • [...]
  • every non-variant non-static data member and base class subobject shall be initialized ([class.base.init]);
  • [...]

Здесь v имеет тип int и не инициализирован. Поэтому кажется, что конструктор A не может быть объявлен constexpr.

Однако [dcl.конструктор]/6 говорит:

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is still a constexpr function or constexpr constructor, even though a call to such a function cannot appear in a constant expression. If no specialization of the template would satisfy the requirements for a constexpr function or constexpr constructor when considered as a non-template function or constructor, the template is ill-formed, no diagnostic required.

Следовательно, конструктор A, объявленный constexpr на самом деле действителен, даже при создании экземпляра для T = int!


Проблема в конструкторе B. B — обычный класс (в отличие от шаблона класса), и чтобы его конструктор был (просто) объявленconstexpr, A<int> должен иметь constexpr конструктор, это не так.

Поэтому этот код следует отвергнуть, как это делает GCC.


(Обратите внимание, что оба компилятора отвергают инициализацию такого типа, Например:

A a{};
B b{};

Приведенный выше код отвергается обоими компиляторами.)

Как упоминалось в комментарий, инициализируйте A::v и GCC (и стандарт) будут счастливы.

Другие вопросы по теме

Должен ли член быть инициализирован, чтобы получить его адрес?
Определение встроенной функции с внешней связью, содержащей ссылку на статический объект
Поведение создания экземпляра шаблона изменяется при использовании явного квалификатора пространства имен?
C++ Неожиданное целочисленное продвижение
В чем причина того, что разрешенное значение undefined из Promise#finally не используется в специальном регистре?
Printf буквальное число (int), ожидая более короткого числа
Есть ли какие-то значимые статистические данные, чтобы оправдать сохранение неопределенного целочисленного арифметического переполнения со знаком?
В чем смысл вычитания двух указателей, не связанных с неопределенным поведением одного и того же массива?
Является ли копи-конструктор жизнеспособной перегрузкой?
C++ использует итераторы, инициализированные по умолчанию, для создания пустого std::string