GCC'de sanal devralma davranışında garip varsayılan boş yapıcı

Dec 14 2020

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() = defaultveya tanımlarsam çalışır.

Neden = defaultbu durumda GCC'deki işlev kaldırılıyor?

Yanıtlar

2 eerorika Dec 14 2020 at 18:29

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.

Derivedsoyuttur (çünkü tüm saf sanal işlevleri uygulamaz) ve Basesanal 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.

1 AdrianMole Dec 14 2020 at 18:02

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 ) = defaultima etmek için aldığı görülüyor - ki bu kesinlikle silinir.DerivedBase

Ancak, bağımlılığı ima eden kaldırmalarla kendi varsayılan kurucunuzu açıkça eklemek Derived() {}.

Bu, Basesı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?