Странный пустой конструктор по умолчанию для поведения виртуального наследования в GCC

У меня есть следующая ситуация класса Derived с виртуальным наследованием базового класса в моем коде:

class Base {
    int x;
public:
    Base(int x): x{x} {}
    virtual void f() = 0;
};

class Derived : public virtual Base  {
  public:
    Derived() = default;
};

class Concrete: public Derived {
public:
    Concrete(): Base{42} {}
    void f() override {}
};

Ссылка: https://godbolt.org/z/bn1EY6

GCC (trunk) выдает следующую ошибку: error: use of deleted function 'Derived::Derived()' в то время как Clang (trunk) компилирует без проблем.

GCC работает, если я изменю конструктор на Derived() {} вместо Derived() = default или определю пустой конструктор в базовом классе.

Почему в этом случае = default удаляет функцию в GCC?

Возможный дубликат: stackoverflow.com/questions/29483120/…

The Philomath 14.12.2020 11:45

Я думаю, что это идет в том же направлении: stackoverflow.com/questions/54030976/…

F K 14.12.2020 11:51

Какую версию gcc вы используете?

U. W. 14.12.2020 11:56

@U.W. это версия магистрали GCC 10. Та же ошибка компиляции появляется в GCC 7 или более поздней версии.

Lucas Marcondes Pavelski 14.12.2020 11:59

@ThePhilomath, спасибо. Добавление noexcept приводит к ошибке internal compiler error: in maybe_explain_implicit_delete, at cp/method.c:2671

Lucas Marcondes Pavelski 14.12.2020 12:01

@LucasMarcondesPavelski Куда вы добавили noexcept? ICE всегда является ошибкой компилятора.

eerorika 14.12.2020 12:16

@eerorika Я добавил в конструктор Derived: Derived() noexcept = default; ссылка godbolt.org/z/5fx8Ph

Lucas Marcondes Pavelski 14.12.2020 12:19
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
7
199
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Почему в этом случае = default удаляет функцию в GCC?

Является ли это ошибкой в ​​GCC (MSVC ведет себя аналогично, но clang-cl принимает код как есть) — это вопрос для тех, кто больше знаком со стандартами C++. Однако похоже, что компилятор использует = default, чтобы подразумевать, что конструктор Derived зависит (или эквивалентен) конструктору по умолчанию для Base, который определенно удален, поскольку вы определили другой конструктор (не по умолчанию).

Однако явное добавление собственного конструктора по умолчанию с помощью Derived() {} удаляет эту подразумеваемую зависимость.

Это подтверждается (в GCC и MSVC) указанием (т. е. восстановлением) конструктора по умолчанию для класса Base:

class Base {
    int x;
public:
    Base() : x{0} {}  // Adding this removes the error!
//  Base() = default; // Also works
    Base(int x): x{x} {}
    virtual void f() = 0;
};

class Derived : public virtual Base  {
  public:
    Derived() = default;
};

class Concrete: public Derived {
public:
    Concrete(): Base{42} {}
    void f() override {}
};

Обновлено: Это также может иметь значение или даже возможный дубликат: Почему конструктор по умолчанию вызывается в виртуальном наследовании?

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

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

[класс.default.ctor]

Конструктор по умолчанию для класса X определяется как удаленный, если:

  • X - это союз, который... [[не применяется]]
  • X — это класс, не являющийся объединением, который имеет вариант члена M с ... [[не применяется]]
  • любой нестатический элемент данных без инициализатора члена по умолчанию ([class.mem]) имеет ссылочный тип, [[не применяется]]
  • любой невариантный нестатический элемент данных константного типа... [[не применяется]]
  • X является объединением и... [[не применяется]]
  • X является классом, не входящим в союз, и все члены любого анонимного члена союза... [[не применяется]]
  • [применяется, если база является потенциально сконструированным подобъектом] любой потенциально сконструированный подобъект, за исключением нестатического члена данных с инициализатором фигурной скобки или равенства, имеет тип класса M (или их массив) и либо M не имеет конструктора по умолчанию, либо перегружен разрешение ([over.match]) применительно к поиску соответствующего M конструктор приводит к неоднозначности или удалению функции или недоступен из конструктора по умолчанию по умолчанию, или
  • любой потенциально созданный подобъект имеет тип с деструктором, который удален или недоступен из конструктора по умолчанию. [[не применяется]]

Только одно правило потенциально применимо к удаляемому конструктору по умолчанию, и оно зависит от того, является ли база потенциально сконструированным подобъектом.

[особенный]

Для класса его нестатические данные-члены, его невиртуальные прямые базовые классы и, если класс не является абстрактным ([class.abstract]), его виртуальные базовые классы называются его потенциально сконструированными подобъектами.

Derived является абстрактным (поскольку он не реализует все чисто виртуальные функции), а Base является виртуальной базой, поэтому база не является потенциально сконструированным подобъектом, и, следовательно, единственное правило, которое в противном случае применялось бы для удаляемого конструктора по умолчанию, делает не применяется, и поэтому его не следует удалять. Компилятор ошибается.


Простой обходной путь (помимо тех, которые вы уже упомянули) — вообще не объявлять Derived::Derieved(). В этом случае он кажется правильно неявно сгенерированным.


Добавление noexcept приводит к ошибке внутренней ошибки компилятора

Это тоже ошибка компилятора.

Хех: те, кто больше изучал стандарты C++ - действительно. Красиво объяснил.

Adrian Mole 14.12.2020 12:38

Спасибо! Я открыл отчет об ошибке в gcc.gnu.org/bugzilla/show_bug.cgi?id=98410

Lucas Marcondes Pavelski 21.12.2020 12:20

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