GCC의 가상 상속 동작에서 이상한 기본 빈 생성자
내 코드에서 Base 클래스에 대한 가상 상속이있는 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 (트렁크)는 다음 오류를 제공합니다. error: use of deleted function 'Derived::Derived()'
Clang (트렁크)은 문제없이 컴파일합니다.
GCC는 생성자를 Derived() {}
대신 변경 Derived() = default
하거나 Base 클래스에서 빈 생성자를 정의하면 작동합니다 .
= default
이 경우 GCC에서 기능 이 제거되는 이유는 무엇 입니까?
답변
표준 내용 (최신 초안) :
[class.default.ctor]
클래스 X의 기본 생성자는 다음과 같은 경우 삭제 된 것으로 정의됩니다.
- X는 다음과 같은 유니온입니다 ... [[적용되지 않음]]
- X는 ... [[적용되지 않음]] 과 함께 변형 멤버 M이있는 비 유니온 클래스입니다 .
- 기본 멤버 이니셜 라이저 ([class.mem])가없는 비 정적 데이터 멤버는 참조 유형입니다. [[적용되지 않음]]
- const 한정 유형의 비 변형 비 정적 데이터 멤버 ... [[적용되지 않음]]
- X는 공용체이고 ... [[적용되지 않음]]
- X는 비 조합 클래스이고 익명의 조합 멤버의 모든 멤버입니다 ... [[적용되지 않음]]
- [ 기본이 잠재적으로 생성 된 하위 객체 인 경우 적용 ] 중괄호 또는 같음 이니셜 라이저가있는 비 정적 데이터 멤버를 제외하고 잠재적으로 생성 된 모든 하위 객체는 클래스 유형 M (또는 그 배열)을 가지며 M에는 기본 생성자가 없습니다. 또는 M의 해당 생성자를 찾기 위해 적용된 오버로드 해결 ([over.match]) 결과 모호함이 발생하거나 기본 기본 생성자에서 삭제되거나 액세스 할 수없는 함수가 발생합니다.
- 잠재적으로 생성 된 모든 하위 개체에는 기본 기본 생성자에서 삭제되거나 액세스 할 수없는 소멸자가있는 형식이 있습니다. [[적용되지 않습니다]]
삭제되는 기본 기본 생성자에 대해 잠재적으로 하나의 규칙 만 적용되며 이는 기본이 잠재적으로 생성 된 하위 객체 인지 여부에 따라 달라집니다 .
[특별한]
클래스의 경우 비 정적 데이터 멤버, 비가 상 직접 기본 클래스 및 클래스가 추상 ([class.abstract]) 이 아닌 경우 해당 가상 기본 클래스를 잠재적으로 생성 된 하위 객체라고합니다.
Derived
(그것은 모든 순수 가상 함수를 구현하지 않기 때문에) 추상적이며, Base
따라서 기본이며, 가상 기본입니다 하지 적용되지 않습니다 삭제되는 잠재적으로 구성된 하위 객체하고, 그렇지 않으면 부도 생성자 신청 한 것 때문에 유일한 규칙 따라서 삭제해서는 안됩니다. 컴파일러가 잘못되었습니다.
간단한 해결 방법 (이미 언급 한 것 외에)은 전혀 선언하지 않는 Derived::Derieved()
것입니다. 이 경우 암시 적으로 올바르게 생성 된 것 같습니다.
noexcept를 추가하면 오류 내부 컴파일러 오류가 발생합니다.
이것은 또한 컴파일러 버그입니다.
이 경우 = default가 GCC에서 함수를 제거하는 이유는 무엇입니까?
이것이 GCC의 버그인지 여부 (MSVC는 유사하게 동작하지만 clang-cl은 코드를 그대로 받아들임)는 C ++ 표준에서 더 많이 연구 된 사람들의 문제입니다. 그러나, 컴파일러는 중임을 표시 = default
것을 의미하는 Derived
생성자에 따라 달라집니다 (또는이다 동등 의 기본 생성자를) Base
하는 - 확실히 다른 (기본이 아닌) 생성자를 정의로, 삭제.
그러나 암시 적 종속성 을 제거 하여 고유 한 기본 생성자를 명시 적으로 추가 합니다Derived() {}
.
이것은 Base
클래스 의 기본 생성자를 지정 (즉, 삭제 취소)함으로써 확인됩니다 (GCC 및 MSVC에서) .
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 {}
};
편집 : 이것은 또한 관련이 있거나 가능한 중복 일 수 있습니다. 왜 가상 상속에서 기본 생성자가 호출됩니까?