Если явно заданный по умолчанию параметр constexpr ctor допускает инициализацию без constexpr - PullRequest
4 голосов
/ 11 мая 2019

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

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

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

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

1 Ответ

6 голосов
/ 11 мая 2019

Проблема сводится к: является конструктором 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;
      |     ^

живая демоверсия

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


За [dcl.constexpr] / 4 :

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

  • класс не должен иметь никаких виртуальных базовых классов;
  • каждый из типов параметров должен быть литеральным типом.

Кроме того, либо тело функции должно быть = delete, либо должен удовлетворять следующим требованиям:

  • [...]
  • каждый не вариантный нестатический член данных и подобъект базового класса инициализируется ([class.base.init]);
  • [...]

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

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

Если конкретизированная специализация шаблона функции constexpr шаблон или функция-член шаблона класса не сможет удовлетворить требования к функции constexpr или конструктору constexpr, эта специализация по-прежнему является функцией constexpr или constexpr конструктор, даже если вызов такой функции не может появиться в постоянное выражение. Если бы не специализация шаблона была бы удовлетворять требованиям для функции constexpr или constexpr конструктор, когда рассматривается как не шаблонная функция или конструктор, шаблон неправильно сформирован, диагностика не требуется.

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


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

Следовательно, этот код должен быть отклонен, как это делает GCC.


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

A a{};
B b{};

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

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

...