C ++ 11, statik olmayan ve const olmayan üyelerin sınıf içi başlatılmasına izin verir. Ne değişti?


89

C ++ 11'den önce, sınıf içi başlatma işlemini yalnızca integral veya numaralandırma türünün statik const üyelerinde gerçekleştirebiliyorduk. Stroustrup bunu C ++ SSS bölümünde tartışır ve aşağıdaki örneği verir:

class Y {
  const int c3 = 7;           // error: not static
  static int c4 = 7;          // error: not const
  static const float c5 = 7;  // error: not integral
};

Ve aşağıdaki mantık:

Öyleyse neden bu uygunsuz kısıtlamalar var? Bir sınıf genellikle bir başlık dosyasında bildirilir ve bir başlık dosyası genellikle birçok çeviri birimine dahil edilir. Ancak, karmaşık bağlayıcı kurallarından kaçınmak için, C ++ her nesnenin benzersiz bir tanıma sahip olmasını gerektirir. C ++, bellekte nesne olarak depolanması gereken varlıkların sınıf içi tanımına izin verirse bu kural bozulur.

Bununla birlikte, C ++ 11 bu kısıtlamaları gevşeterek statik olmayan üyelerin sınıf içi başlatılmasına izin verir (§12.6.2 / 8):

A ile veri üyesi veya temel sınıf statik olmayan belirtilmemişse verilirse olmayan bir delegating yapıcısı olarak, Mem-başlatıcı-id (durumda da dahil olmak üzere hiçbir olduğu yerde mem-başlatıcısı listesinden yapıcısı hayır çünkü ctor-başlatıcı ) ve varlık, soyut bir sınıfın (10.4) sanal bir temel sınıfı değilse

  • varlık, bir küme ayracı veya eşitleme başlatıcısına sahip statik olmayan bir veri üyesi ise, varlık 8.5'te belirtildiği gibi başlatılır;
  • aksi takdirde, varlık bir değişken üye (9.5) ise, başlatma gerçekleştirilmez;
  • aksi takdirde, varlık temerrüt olarak başlatılmıştır (8.5).

Bölüm 9.4.2 ayrıca, constexprbelirtici ile işaretlenmişlerse const olmayan statik üyelerin sınıf içi başlatılmasına da izin verir .

Peki C ++ 03'te sahip olduğumuz kısıtlamaların nedenlerine ne oldu? Sadece "karmaşık bağlayıcı kuralları" kabul ediyor muyuz yoksa bunun uygulanmasını kolaylaştıran başka bir şey mi değişti?


5
Hiçbir şey olmadı. Derleyiciler tüm bu salt başlık şablonlarıyla daha akıllı hale geldi, bu nedenle artık nispeten kolay bir uzantı.
Öö Tiib

C ++ 11 öncesi derlemeyi seçtiğimde IDE'mde yeterince ilginç bir şekilde statik olmayan const integral üyelerini başlatmama izin veriyorum
Dean P

Yanıtlar:


67

Kısa cevap, derleyiciyi öncekinden daha karmaşık hale getirme pahasına, bağlayıcıyı hemen hemen aynı tutmalarıdır.

Yani, bunun bağlayıcının sıralaması için birden fazla tanımla sonuçlanması yerine, yine de yalnızca bir tanımla sonuçlanır ve derleyicinin bunu çözmesi gerekir.

Bu aynı zamanda programcının sıralı tutması için biraz daha karmaşık kurallara da yol açar , ancak çoğunlukla yeterince basittir ki, önemli değildir. Tek bir üye için iki farklı başlatıcı belirlediğinizde ekstra kurallar devreye girer:

class X { 
    int a = 1234;
public:
    X() = default;
    X(int z) : a(z) {}
};

Şimdi, bu noktadaki ekstra kurallar, avarsayılan olmayan yapıcıyı kullandığınızda başlatmak için hangi değerin kullanıldığını ele alır . Bunun cevabı oldukça basittir: Başka bir değer belirtmeyen bir kurucu kullanırsanız, o zaman 1234başlatmak için kullanılır a- ancak başka bir değer belirten bir kurucu kullanırsanız 1234, temelde yok sayılır.

Örneğin:

#include <iostream>

class X { 
    int a = 1234;
public:
    X() = default;
    X(int z) : a(z) {}

    friend std::ostream &operator<<(std::ostream &os, X const &x) { 
        return os << x.a;
    }
};

int main() { 
    X x;
    X y{5678};

    std::cout << x << "\n" << y;
    return 0;
}

Sonuç:

1234
5678

1
Görünüşe göre bu daha önce oldukça mümkündü. Sadece bir derleyici yazma işini zorlaştırdı. Bu adil bir ifade mi?
allyourcode

10
@allyourcode: Evet ve hayır. Evet, derleyiciyi yazmayı zorlaştırdı. Ama hayır, çünkü aynı zamanda C ++ şartname biraz daha sert yazmaya yaptı.
Jerry Tabut

Sınıf üyesinin nasıl başlatılacağı konusunda bir fark var mı: int x = 7; veya int x {7} ;?
mbaros

9

Sanırım akıl yürütme şablonlar tamamlanmadan önce yazılmış olabilir. Sonuçta, statik üyelerin sınıf içi başlatıcıları için gerekli olan "karmaşık bağlayıcı kural (lar)", şablonların statik üyelerini desteklemek için C ++ 11 için gerekliydi / zaten gerekliydi.

Düşünmek

struct A { static int s = ::ComputeSomething(); }; // NOTE: This isn't even allowed,
                                                   // thanks @Kapil for pointing that out

// vs.

template <class T>
struct B { static int s; }

template <class T>
int B<T>::s = ::ComputeSomething();

// or

template <class T>
void Foo()
{
    static int s = ::ComputeSomething();
    s++;
    std::cout << s << "\n";
}

Derleyici için sorun her üç durumda da aynıdır: hangi çeviri biriminde tanımını sve onu başlatmak için gerekli kodu yayınlamalı? Basit çözüm, onu her yere yaymak ve bağlayıcının çözmesine izin vermektir. Bu yüzden bağlayıcılar zaten bu gibi şeyleri destekledi __declspec(selectany). C ++ 03'ü onsuz uygulamak mümkün olamazdı. İşte bu yüzden bağlayıcıyı genişletmek gerekli değildi.

Daha açık bir şekilde söylemek gerekirse: Eski standartta verilen muhakemenin tamamen yanlış olduğunu düşünüyorum.


GÜNCELLEME

Kapil'in belirttiği gibi, benim ilk örneğime mevcut standartta (C ++ 14) bile izin verilmiyor. Yine de bıraktım, çünkü IMO uygulama için en zor durumdur (derleyici, bağlayıcı). Demek istediğim şu: bu durum bile zaten izin verilenden daha zor değil, örneğin şablonlar kullanılırken.


C ++ 11 özelliklerinin çoğu, derleyicilerin gerekli yetenek veya optimizasyonları zaten içermesi bakımından benzer olduğu için, bunun herhangi bir olumlu oy almaması utanç verici.
Alex Court

@AlexCourt Bu cevabı geçenlerde yazdım. Soru ve Jerry'nin cevabı 2012'den. Sanırım bu yüzden cevabım fazla ilgi görmedi.
Paul Groke

1
Bu, "struct A {static int s = :: ComputeSomething ();}"
ifadesini tamamlamayacaktır

8

Teoride So why do these inconvenient restrictions exist?...mantık geçerlidir, ancak kolaylıkla atlanabilir ve C ++ 11'in yaptığı tam olarak budur.

Ne zaman dahil bir dosyayı, sadece dosya ve umursamadığını herhangi başlatma kapsamaktadır. Ne zaman sadece üyeler başlatılır örneğini sınıfı.

Başka bir deyişle, başlatma hala yapıcı ile bağlantılıdır, sadece gösterim farklı ve daha uygundur. Yapıcı çağrılmazsa, değerler başlatılmaz.

Yapıcı çağrılırsa, değerler varsa sınıf içi başlatma ile başlatılır veya kurucu bunu kendi başlatma ile geçersiz kılabilir. Başlatma yolu temelde aynıdır, yani kurucu aracılığıyla.

Bu Stroustrup kendi bellidir SSS C ++ 11.


"Kurucu çağrılmazsa, değerler başlatılmaz": Sorudaki üye başlatmayı nasıl atlatabilirim Y::c3? Anladığım kadarıyla c3, bildirimde verilen varsayılanı geçersiz kılan bir kurucu olmadığı sürece her zaman başlatılacaktır.
Peter - Monica'yı eski
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.