У меня есть следующая ситуация класса 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/54030976/…
Какую версию gcc вы используете?
@U.W. это версия магистрали GCC 10. Та же ошибка компиляции появляется в GCC 7 или более поздней версии.
@ThePhilomath, спасибо. Добавление noexcept приводит к ошибке internal compiler error: in maybe_explain_implicit_delete, at cp/method.c:2671
@LucasMarcondesPavelski Куда вы добавили noexcept? ICE всегда является ошибкой компилятора.
@eerorika Я добавил в конструктор Derived: Derived() noexcept = default; ссылка godbolt.org/z/5fx8Ph





Почему в этом случае = 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++ - действительно. Красиво объяснил.
Спасибо! Я открыл отчет об ошибке в gcc.gnu.org/bugzilla/show_bug.cgi?id=98410
Возможный дубликат: stackoverflow.com/questions/29483120/…