У меня есть следующая ситуация класса 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/…