GCC'de sanal devralma davranışında garip varsayılan boş yapıcı
Kodumda bir Base sınıfına sanal miras içeren bir Derived sınıfının aşağıdaki durumu var:
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 {}
};
Bağlantı: https://godbolt.org/z/bn1EY6
GCC (ana hat) şu hatayı verir: error: use of deleted function 'Derived::Derived()'
Clang (gövde) ise sorunsuz bir şekilde derler.
GCC, yapıcıyı Base sınıfında boş bir oluşturucu Derived() {}
yerine olarak değiştirirsem Derived() = default
veya tanımlarsam çalışır.
Neden = default
bu durumda GCC'deki işlev kaldırılıyor?
Yanıtlar
Standart diyor ki (son taslak):
[class.default.ctor]
X sınıfı için varsayılan bir varsayılan kurucu, şu durumlarda silinmiş olarak tanımlanır:
- X bir birliktir ... [[geçerli değildir]]
- X, ... [[geçerli değildir]] ile M değişken üyesine sahip sendika dışı bir sınıftır
- varsayılan üye başlatıcısı olmayan herhangi bir statik olmayan veri üyesi ([class.mem]) başvuru türündedir, [[geçerli değildir]]
- sabit nitelikli herhangi bir değişken olmayan statik olmayan veri üyesi ... [[geçerli değildir]]
- X bir birliktir ve ... [[geçerli değildir]]
- X, sendika dışı bir sınıftır ve herhangi bir anonim sendika üyesinin tüm üyeleri ... [[geçerli değildir]]
- [ taban potansiyel olarak oluşturulmuş bir alt nesne ise geçerlidir ] herhangi bir potansiyel olarak oluşturulmuş alt nesne, bir parantez veya eşitleme başlatıcısı olan statik olmayan bir veri üyesi hariç, M sınıfı (veya bunun dizisi) vardır ve M'nin varsayılan kurucusu yoktur veya M'nin karşılık gelen yapıcısını bulmak için uygulandığı şekliyle aşırı yük çözünürlüğü ([over.match]), bir belirsizliğe veya varsayılan varsayılan kurucudan silinen veya erişilemeyen bir işlevle sonuçlanır veya
- potansiyel olarak oluşturulmuş herhangi bir alt nesne, varsayılan varsayılan kurucudan silinen veya erişilemeyen bir yıkıcıya sahip bir türe sahiptir. [[geçerli değildir]]
Varsayılan olarak silinen varsayılan kurucu için potansiyel olarak yalnızca bir kural geçerlidir ve bu, tabanın potansiyel olarak oluşturulmuş bir alt nesne olup olmadığına bağlıdır .
[özel]
Bir sınıf için statik olmayan veri üyeleri, sanal olmayan doğrudan temel sınıfları ve sınıf soyut değilse ([class.abstract]), sanal temel sınıfları potansiyel olarak oluşturulmuş alt nesneleri olarak adlandırılır.
Derived
soyuttur (çünkü tüm saf sanal işlevleri uygulamaz) ve Base
sanal bir temeldir, bu nedenle taban potansiyel olarak oluşturulmuş bir alt nesne değildir ve bu nedenle, aksi takdirde silinen varsayılan kurucu için uygulanacak tek kural geçerli değildir ve bu nedenle silinmemelidir. Derleyici yanlış.
Basit bir çözüm (daha önce bahsettiklerinizin yanı sıra) hiç beyan etmemek Derived::Derieved()
. Bu durumda dolaylı olarak doğru şekilde oluşturulmuş gibi görünüyor.
Noexcept'i eklemek dahili derleyici hatası verir
Bu aynı zamanda bir derleyici hatasıdır.
Neden bu durumda = default işlevi GCC'deki işlevi kaldırıyor?
Bunun GCC'de bir hata olup olmadığı (MSVC benzer şekilde davranır ancak clang-cl kodu olduğu gibi kabul eder) C ++ Standartlarında daha fazla çalışılanlar için bir konudur. Ancak, başka bir (varsayılan olmayan) kurucu tanımladığınız için , derleyicinin kurucunun varsayılan kurucuya bağlı olduğunu (veya buna eşdeğer olduğunu ) = default
ima etmek için aldığı görülüyor - ki bu kesinlikle silinir.Derived
Base
Ancak, bağımlılığı ima eden kaldırmalarla kendi varsayılan kurucunuzu açıkça eklemek Derived() {}
.
Bu, Base
sınıf için varsayılan kurucuyu belirterek (yani silme işlemini geri alarak) onaylanır (GCC ve MSVC'de) :
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 {}
};
DÜZENLEME : Bu aynı zamanda alakalı veya hatta olası bir kopya olabilir: Varsayılan kurucu neden sanal miras olarak çağrılıyor?