Agregalar ve POD'lar nedir ve nasıl / neden özeldir?


548

Bu SSS , Agregalar ve POD'lar ile ilgilidir ve aşağıdaki malzemeleri kapsar:

  • Nelerdir Agregalar ?
  • Nelerdir POD s (Düz Eski Veriler)?
  • İlişkileri nasıl?
  • Nasıl ve neden özel?
  • C ++ 11 için ne değişir?


Bu tanımların ardındaki motivasyonun kabaca olduğu söylenebilir: POD == memcpy'able, Aggregate == toplam-başlatılabilir mi?
Ofek Shilon

Yanıtlar:


571

Nasıl okunur:

Bu makale oldukça uzun. Hem agregaları hem de POD'ları (Düz Eski Veriler) bilmek istiyorsanız, zaman ayırın ve okuyun. Sadece agregalarla ilgileniyorsanız, sadece ilk kısmı okuyun. Yalnızca PODs ilgilenen varsa o zaman ilk agrega tanımı, etkileri ve örnekler okumalı ve sonra olabilecek PODs atlamak ama hala kendi bütünlüğü içinde birinci bölümünü okuduğunuz öneriyoruz. Agrega kavramı POD'ları tanımlamak için gereklidir. Herhangi bir hata bulursanız (dilbilgisi, üslup, biçimlendirme, sözdizimi vb. Dahil olmak üzere küçük bile olsa) lütfen yorum bırakın, düzenleyeceğim.

Bu cevap C ++ 03 için geçerlidir. Diğer C ++ standartları için bakınız:

Agrega nedir ve neden özeldir?

C ++ standardından biçimsel tanım ( C ++ 03 8.5.1 §1 ) :

Toplama, kullanıcı tarafından bildirilen kurucuları (12.1), özel veya korumalı statik olmayan veri üyeleri (madde 11), temel sınıflar (madde 10) ve sanal işlevler (10.3) bulunmayan bir dizi veya sınıftır (madde 9). ).

Tamam, bu tanımı ayrıştıralım. Her şeyden önce, herhangi bir dizi bir kümedir. Bir sınıf aynı zamanda… bekle! yapılar veya sendikalar hakkında hiçbir şey söylenmez, bunlar küme olamazlar mı? Evet yapabilirler. C ++ 'da, terim classtüm sınıfları, yapıları ve birlikleri ifade eder. Dolayısıyla, bir sınıf (ya da yapı ya da birlik), yalnızca yukarıdaki tanımlardan gelen kriterleri karşıladığında toplanır. Bu kriterler ne anlama geliyor?

  • Bu, bir toplu sınıfın yapıcılara sahip olamayacağı anlamına gelmez, aslında derleyici tarafından açıkça belirtildiği ve kullanıcı tarafından açıkça belirtilmediği sürece varsayılan bir kurucuya ve / veya bir kopya kurucuya sahip olabileceği anlamına gelmez.

  • Özel veya korumalı statik olmayan veri üyeleri yok . Toplama sınıflarının kurallarını ihlal ettiğiniz gibi istediğiniz kadar özel ve korumalı üye işlevinizin (ancak yapıcıların değil) yanı sıra istediğiniz kadar özel veya korumalı statik veri üyesine ve üye işlevine sahip olabilirsiniz

  • Bir toplu sınıf, kullanıcı tarafından bildirilen / kullanıcı tanımlı bir kopya atama işleci ve / veya yıkıcıya sahip olabilir

  • Bir dizi, birleştirilmiş olmayan sınıf türü dizisi olsa bile bir toplamdır.

Şimdi bazı örneklere bakalım:

class NotAggregate1
{
  virtual void f() {} //remember? no virtual functions
};

class NotAggregate2
{
  int x; //x is private by default and non-static 
};

class NotAggregate3
{
public:
  NotAggregate3(int) {} //oops, user-defined constructor
};

class Aggregate1
{
public:
  NotAggregate1 member1;   //ok, public member
  Aggregate1& operator=(Aggregate1 const & rhs) {/* */} //ok, copy-assignment  
private:
  void f() {} // ok, just a private function
};

Kaptın bu işi. Şimdi toplamların nasıl özel olduğunu görelim. Agrega olmayan sınıflardan farklı olarak, kıvırcık parantezlerle başlatılabilirler {}. Bu başlatma sözdizimi, diziler için yaygın olarak bilinir ve bunların toplamlar olduğunu öğrendik. Öyleyse onlarla başlayalım.

Type array_name[n] = {a1, a2, …, am};

(m == n) ise
i inci dizinin elemanı ile başlatılır ı
başka, eğer (m, <n)
dizinin ilk m elemanları ile başlatılır 1 , bir 2 , ..., bir m ve diğern - melemanlar mümkünse vardır değer başlatıldı (terimin açıklama için aşağıya bakınız)
else if (m> n)
bir hata mesajı üretir derleyici
başka (bu durumda olduğunda n gibi hiç belirtilmemişse int a[] = {1, 2, 3};)
boyutunu dizi (n), böylece buna eşit olduğu varsayılırint a[] = {1, 2, 3};eşdeğerdirint a[3] = {1, 2, 3};

Skalar tip (bir amacı zaman bool, int, char, double, işaretçiler, vs.) değer başlatıldı bunun ile başlatılır anlamına gelir 0, bu tür için ( falseiçin bool, 0.0için double, vs.). Kullanıcı tarafından bildirilen bir varsayılan kurucuya sahip sınıf türünde bir nesne değer-başlatıldığında, varsayılan kurucu çağrılır. Varsayılan kurucu örtük olarak tanımlanmışsa, tüm statik olmayan üyeler özyinelemeli olarak değer başlatılır. Bu tanım kesin değildir ve biraz yanlıştır, ancak size temel fikri vermelidir. Bir başvuru, değer başlatılamaz. Birleştirilmiş olmayan bir sınıf için değer başlatma, örneğin sınıfın uygun bir varsayılan kurucusu yoksa başarısız olabilir.

Dizi başlatma örnekleri:

class A
{
public:
  A(int) {} //no default constructor
};
class B
{
public:
  B() {} //default constructor available
};
int main()
{
  A a1[3] = {A(2), A(1), A(14)}; //OK n == m
  A a2[3] = {A(2)}; //ERROR A has no default constructor. Unable to value-initialize a2[1] and a2[2]
  B b1[3] = {B()}; //OK b1[1] and b1[2] are value initialized, in this case with the default-ctor
  int Array1[1000] = {0}; //All elements are initialized with 0;
  int Array2[1000] = {1}; //Attention: only the first element is 1, the rest are 0;
  bool Array3[1000] = {}; //the braces can be empty too. All elements initialized with false
  int Array4[1000]; //no initializer. This is different from an empty {} initializer in that
  //the elements in this case are not value-initialized, but have indeterminate values 
  //(unless, of course, Array4 is a global array)
  int array[2] = {1, 2, 3, 4}; //ERROR, too many initializers
}

Şimdi toplu sınıfların kaşlı ayraçlarla nasıl başlatılabileceğini görelim. Hemen hemen aynı şekilde. Dizi elemanları yerine, statik olmayan veri üyelerini sınıf tanımındaki görünümlerine göre başlatacağız (hepsi tanım gereği herkese açıktır). Üyelerden daha az başlatıcı varsa, geri kalanı değerle başlatılır. Açık bir şekilde başlatılmamış üyelerden birine değer vermek mümkün değilse, derleme zamanı hatası alırız. Gerekenden daha fazla başlatıcı varsa, derleme zamanı hatası da alırız.

struct X
{
  int i1;
  int i2;
};
struct Y
{
  char c;
  X x;
  int i[2];
  float f; 
protected:
  static double d;
private:
  void g(){}      
}; 

Y y = {'a', {10, 20}, {20, 30}};

Yukarıdaki örnekte y.c, ile 'a', y.x.i1ile 10, y.x.i2ile 20, y.i[0]ile 20, y.i[1]ile 30ve y.fdeğerle başlatılır, yani ile başlatılır 0.0. Korumalı statik üye dhiç başlatılmaz, çünkü öyle static.

Toplu sendikalar farklıdır, çünkü yalnızca ilk üyelerini parantezle başlatabilirsiniz. C ++ 'da sendika kullanmayı düşünecek kadar gelişmişseniz (kullanımları çok tehlikeli olabilir ve dikkatlice düşünülmelidir), standarttaki sendikaların kurallarına kendiniz bakabilirsiniz :).

Toplamlar hakkında neyin özel olduğunu bildiğimize göre, sınıflardaki kısıtlamaları anlamaya çalışalım; işte bu yüzden neden oradalar. Parantez ile üye olarak başlatmanın, sınıfın üyelerinin toplamından başka bir şey olmadığını ima ettiğini anlamalıyız. Kullanıcı tanımlı bir kurucu varsa, kullanıcının üyeleri başlatmak için fazladan bir iş yapması gerektiği anlamına gelir, bu nedenle küme ayracı başlatma yanlış olur. Sanal işlevler varsa, bu sınıftaki nesnelerin (çoğu uygulamada) yapıcıda ayarlanan sınıfın vtable'ı için bir işaretçi olduğu anlamına gelir, bu nedenle küme parantezinin başlatılması yetersiz olacaktır. Geri kalan kısıtlamaları bir egzersize benzer şekilde anlayabilirsiniz :).

Agregalar hakkında yeterli. Şimdi, POD'lara daha katı türler tanımlayabiliriz

POD'lar nedir ve neden özeldir?

C ++ standardından biçimsel tanım ( C ++ 03 9 §4 ) :

POD-yapı, POD-struct olmayan, POD-birliği olmayan (veya bu tür bir dizi) veya başvuru türünde statik olmayan veri üyelerine sahip olmayan ve kullanıcı tanımlı bir kopya atama operatörü olmayan ve hiçbir kullanıcı tanımlı yıkıcı. Benzer şekilde, POD-birliği, POD-yapı olmayan, POD-birliği olmayan (veya bu tür bir dizi) veya başvuru türünde statik olmayan veri üyelerine sahip olmayan ve kullanıcı tanımlı bir kopya atama operatörü olmayan bir toplu birliktir ve kullanıcı tanımlı yıkıcı yok. POD sınıfı, POD yapısı veya POD birleşimi olan bir sınıftır.

Vay canına, bu ayrıştırmak daha zor, değil mi? :) Sendikaları dışarıda bırakalım (yukarıdakiyle aynı gerekçelerle) ve biraz daha açık bir şekilde yeniden ifade edelim:

Kullanıcı sınıfı tanımlı bir kopya atama işleci ve yıkıcısı yoksa ve statik olmayan üyelerinden hiçbiri POD olmayan bir sınıf, POD olmayan bir dizi veya bir başvuru değilse, toplu sınıf POD olarak adlandırılır.

Bu tanım ne anlama geliyor? ( POD'un Eski Düz Veri anlamına geldiğinden bahsetmiş miydim ?)

  • Tüm POD sınıfları topludır veya başka bir deyişle, sınıf bir toplu değilse, POD olmadığından emin olur
  • Sınıflar, tıpkı yapılar gibi, standart terim her iki durum için de POD yapısı olsa da POD olabilir
  • Toplamalarda olduğu gibi, sınıfın hangi statik üyelere sahip olduğu önemli değildir

Örnekler:

struct POD
{
  int x;
  char y;
  void f() {} //no harm if there's a function
  static std::vector<char> v; //static members do not matter
};

struct AggregateButNotPOD1
{
  int x;
  ~AggregateButNotPOD1() {} //user-defined destructor
};

struct AggregateButNotPOD2
{
  AggregateButNotPOD1 arrOfNonPod[3]; //array of non-POD class
};

POD sınıfları, POD-birlikleri, skaler tipler ve bu tipteki diziler topluca POD-tipleri olarak adlandırılır .
POD'lar birçok açıdan özeldir. Sadece bazı örnekler vereceğim.

  • POD sınıfları C yapılarına en yakın olanlardır. Bunlardan farklı olarak, POD'larda üye işlevleri ve rastgele statik üyeler olabilir, ancak bu ikisinden hiçbiri nesnenin bellek düzenini değiştirmez. Bu nedenle, C ve hatta .NET'ten kullanılabilecek az çok taşınabilir bir dinamik kitaplık yazmak istiyorsanız, dışa aktarılan tüm işlevlerinizi yalnızca POD tipi parametrelerini alıp döndürmeye çalışmalısınız.

  • POD sınıfı olmayan türdeki nesnelerin ömrü, kurucu tamamlandığında başlar ve yıkıcı tamamlandığında sona erer. POD sınıfları için ömür, nesne için depolama alanı dolduğunda başlar ve bu depolama alanı bırakıldığında veya yeniden kullanıldığında sona erer.

  • POD türündeki nesneler için, standart tarafından, memcpynesnenizin içeriğini bir char veya unsigned char dizisine ve daha memcpysonra içeriğin nesnenize geri döndüğünde, nesnenin orijinal değerini koruyacağı garanti edilir . POD olmayan türler için böyle bir garanti olmadığını unutmayın. Ayrıca, POD nesnelerini ile güvenle kopyalayabilirsiniz memcpy. Aşağıdaki örnekte T'nin POD tipi olduğu varsayılmaktadır:

    #define N sizeof(T)
    char buf[N];
    T obj; // obj initialized to its original value
    memcpy(buf, &obj, N); // between these two calls to memcpy,
    // obj might be modified
    memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type
    // holds its original value
    
  • ifadeye git. Bildiğiniz gibi, bazı değişkenlerin henüz kapsamda olmadığı bir noktadan halihazırda kapsamda olan bir noktaya goto aracılığıyla bir sıçrama yapmak yasadışıdır (derleyici bir hata vermelidir). Bu kısıtlama yalnızca değişken POD olmayan türdeyse geçerlidir. Aşağıdaki örnekte iyi f()biçimlendirilmiş, kötü g()biçimlendirilmiş. Microsoft'un derleyicisinin bu kuralla çok liberal olduğunu unutmayın; her iki durumda da bir uyarı verir.

    int f()
    {
      struct NonPOD {NonPOD() {}};
      goto label;
      NonPOD x;
    label:
      return 0;
    }
    
    int g()
    {
      struct POD {int i; char c;};
      goto label;
      POD x;
    label:
      return 0;
    }
    
  • Bir POD nesnesinin başlangıcında dolgu olmayacağı garanti edilir. POD sınıfı A'nın birinci eleman türü T ise, başka bir deyişle, güvenli bir şekilde olabilir reinterpret_castgelen A*için T*işaretçi ve elde tersi birinci eleman ve yardımcısı.

Liste uzayıp gidiyor…

Sonuç

Bir POD'un tam olarak ne olduğunu anlamak önemlidir, çünkü gördüğünüz gibi birçok dil özelliği onlar için farklı davranır.


3
Güzel cevap. Yorumlar: "Varsayılan kurucu örtük olarak tanımlanmışsa, tüm statik olmayan üyeler özyinelemeli olarak değer başlatılır." ve "Örneğin, sınıfın uygun bir varsayılan kurucusu yoksa, toplu olmayan bir sınıf için değer başlatma başarısız olabilir." doğru değil: Bir sınıfın örtülü olarak bildirilmiş bir varsayılan kurucu ile değer başlatılması, örtük olarak tanımlanmış bir varsayılan kurucu gerektirmez. Bu durumda, (uç verilen private:uygun olarak): struct A { int const a; };o zaman A()bile, iyi oluşturulmuş olan Abireyin varsayılan yapıcı tanımı kötü şekillendirilmiş olur.
Johannes Schaub - litb

4
@Kev: Aynı bilgileri daha kısa bir cevapta toplamayı başarırsanız, hepimiz mutlu bir şekilde oy kullanırız!
sbi

3
@Armen, aynı soruya birden fazla cevap verebileceğinizi de unutmayın. Her cevap, sorunun çözümünün bir kısmını içerebilir. Benim fikrime göre kabul edilen işaretli bir şey vida :)
Johannes Schaub - litb

3
Cevap harika. Hala bu gönderiyi birkaç kez tekrar ziyaret ediyorum. Bu arada Visual Studio için uyarılar hakkında. Pod için "goto deyimi", daha önce de belirttiğiniz gibi MSVC derleyicisine cehaletle gelir. Ancak switch / case deyimi için derleme hatası oluşturur. Buna dayanarak bazı test pod denetleyicisi
yaptım

2
"POD sınıfı olmayan türdeki nesnelerin ömrü, kurucu bittiğinde başlar ve yıkıcı bittiğinde biter" ile başlayan madde işaret noktasında. bunun yerine son kısım "yıkıcı başladığında" demelidir.
Quokka

457

C ++ 11 için ne değişir?

Agregalar

Bir agreganın standart tanımı biraz değişti, ancak yine de hemen hemen aynı:

Toplama, kullanıcı tarafından sağlanan kurucuları (12.1), statik olmayan veri üyeleri için küme ayracı veya eşit başlatıcıları olmayan (9.2), özel veya korumalı statik olmayan veri üyeleri olmayan bir dizi veya sınıftır (Madde 9). Madde 11), temel sınıflar (Madde 10) ve sanal işlevler (10.3) yoktur.

Tamam, ne değişti?

  1. Önceden, bir topluluğun kullanıcı tarafından bildirilen kurucuları olamazdı , ancak şimdi kullanıcı tarafından sağlanan kuruculara sahip olamaz . Bir fark var mı? Evet, var, çünkü şimdi yapıcıları bildirebilir ve varsayılan yapabilirsiniz :

    struct Aggregate {
        Aggregate() = default; // asks the compiler to generate the default implementation
    };
    

    İlk bildirimde varsayılan olarak ayarlanan bir kurucu (veya herhangi bir özel üye işlevi) kullanıcı tarafından sağlanmadığından, bu yine de bir toplamdır .

  2. Artık bir toplamanın statik olmayan veri üyeleri için herhangi bir küme ayracı veya eşit başlatıcısı olamaz . Ne anlama geliyor? Bunun nedeni, bu yeni standartla üyeleri doğrudan sınıfta şöyle başlatabilmemizdir:

    struct NotAggregate {
        int x = 5; // valid in C++11
        std::vector<int> s{1,2,3}; // also valid
    };
    

    Bu özelliği kullanmak, sınıfı artık birleştirilmiş hale getirmez, çünkü temel olarak kendi varsayılan kurucunuzu sağlamaya eşdeğerdir.

Yani, toplam olan şey pek değişmedi. Yeni özelliklere uyarlanmış hala aynı temel fikir.

POD'lar ne olacak?

POD'lar birçok değişiklik geçirdi. POD'lar hakkında önceki kuralların birçoğu bu yeni standartta gevşetildi ve standartta tanımın sunulma şekli kökten değiştirildi.

Bir POD fikri temel olarak iki farklı özelliği yakalamaktır:

  1. Statik başlatmayı destekler ve
  2. C ++ 'da POD derlemek size C'de derlenen yapı ile aynı bellek düzenini verir.

Bu nedenle, tanım iki farklı kavrama ayrılmıştır: önemsiz sınıflar ve standart düzen sınıfları, çünkü bunlar POD'dan daha kullanışlıdır. Standart artık nadiren POD terimini kullanıyor ve daha özel önemsiz ve standart düzen kavramlarını tercih ediyor .

Yeni tanım temelde bir POD'un hem önemsiz hem de standart düzeni olan bir sınıf olduğunu ve bu özelliğin tüm statik olmayan veri üyeleri için özyinelemeli olması gerektiğini söylüyor:

POD yapısı, hem önemsiz bir sınıf hem de standart mizanpaj sınıfı olan ve POD olmayan yapı, POD olmayan birleşme (veya bu tür bir dizi) türünde statik olmayan veri üyelerine sahip olmayan birleşik sınıftır. Benzer şekilde, bir POD birliği, hem önemsiz bir sınıf hem de standart bir düzen sınıfı olan ve POD olmayan yapı, POD olmayan birleşme (veya bu tür bir dizi) türünde statik olmayan veri üyelerine sahip olmayan bir birliktir. POD sınıfı, POD yapısı veya POD birleşimi olan bir sınıftır.

Bu iki özelliğin her birini ayrı ayrı ayrıntılı olarak inceleyelim.

Önemsiz sınıflar

Önemsiz yukarıda belirtilen ilk özelliktir: önemsiz sınıflar statik başlatmayı destekler. Bir sınıf önemsiz bir şekilde taklit edilebilirse (önemsiz sınıfların bir üst kümesi), yerdeki temsilini benzer şeylerle kopyalamak ve memcpysonucun aynı olmasını beklemek uygundur.

Standart önemsiz bir sınıfı aşağıdaki gibi tanımlar:

Önemsiz olarak kopyalanabilen bir sınıf:

- önemsiz olmayan kopya kurucuları yoktur (12.8),

- önemsiz olmayan hareket kurucuları yoktur (12.8),

- önemsiz olmayan kopya atama işleçleri yoktur (13.5.3, 12.8),

- önemsiz olmayan hareket atama operatörleri yok (13.5.3, 12.8) ve

- önemsiz bir yıkıcıya sahiptir (12.4).

Önemsiz bir sınıf, önemsiz bir varsayılan yapıcıya (12.1) sahip olan ve önemsiz bir şekilde kopyalanabilen bir sınıftır.

[ Not: Özellikle önemsiz bir şekilde kopyalanabilen veya önemsiz bir sınıfın sanal işlevleri veya sanal temel sınıfları yoktur. —End not ]

Peki, tüm bu önemsiz ve önemsiz şeyler nelerdir?

X sınıfı için bir kopyala / taşı yapıcısı, kullanıcı tarafından sağlanmadıysa ve

- X sınıfı sanal işlevlere (10.3) ve sanal temel sınıflara (10.1) sahip değildir ve

- her doğrudan temel sınıf alt nesnesini kopyalamak / taşımak için seçilen yapıcı önemsizdir ve

- X'in sınıf tipi (veya dizisi) statik olmayan her veri elemanı için, o üyeyi kopyalamak / taşımak için seçilen yapıcı önemsizdir;

aksi takdirde kopyalama / taşıma yapıcısı önemsiz değildir.

Temel olarak bu, bir copy veya move yapıcısının kullanıcı tarafından sağlanan değilse önemsiz olduğu, sınıfın içinde sanal bir şey olmadığı ve bu özelliğin sınıfın tüm üyeleri ve temel sınıf için özyinelemeli olduğu anlamına gelir.

Önemsiz bir kopyala / taşı atama operatörünün tanımı çok benzerdir, sadece "yapıcı" kelimesini "atama operatörü" ile değiştirir.

Önemsiz bir yıkıcı da benzer bir tanıma sahiptir ve ek kısıtlaması sanal olamaz.

Ve yine, önemsiz varsayılan kurucular için benzer bir kural daha vardır; ayrıca, sınıfta yukarıda gördüğümüz küme ayracı veya eşit başlatıcılara sahip statik olmayan veri üyeleri varsa, varsayılan bir kurucu önemsiz değildir .

Her şeyi temizlemek için bazı örnekler:

// empty classes are trivial
struct Trivial1 {};

// all special members are implicit
struct Trivial2 {
    int x;
};

struct Trivial3 : Trivial2 { // base class is trivial
    Trivial3() = default; // not a user-provided ctor
    int y;
};

struct Trivial4 {
public:
    int a;
private: // no restrictions on access modifiers
    int b;
};

struct Trivial5 {
    Trivial1 a;
    Trivial2 b;
    Trivial3 c;
    Trivial4 d;
};

struct Trivial6 {
    Trivial2 a[23];
};

struct Trivial7 {
    Trivial6 c;
    void f(); // it's okay to have non-virtual functions
};

struct Trivial8 {
     int x;
     static NonTrivial1 y; // no restrictions on static members
};

struct Trivial9 {
     Trivial9() = default; // not user-provided
      // a regular constructor is okay because we still have default ctor
     Trivial9(int x) : x(x) {};
     int x;
};

struct NonTrivial1 : Trivial3 {
    virtual void f(); // virtual members make non-trivial ctors
};

struct NonTrivial2 {
    NonTrivial2() : z(42) {} // user-provided ctor
    int z;
};

struct NonTrivial3 {
    NonTrivial3(); // user-provided ctor
    int w;
};
NonTrivial3::NonTrivial3() = default; // defaulted but not on first declaration
                                      // still counts as user-provided
struct NonTrivial5 {
    virtual ~NonTrivial5(); // virtual destructors are not trivial
};

Standart düzen

Standart mizanpaj ikinci özelliktir. Standart, bunların diğer dillerle iletişim kurmak için yararlı olduğunu belirtmektedir ve bunun nedeni, standart düzen sınıfının, eşdeğer C yapısının veya birleşiminin aynı bellek düzenine sahip olmasıdır.

Bu, üyeler ve tüm temel sınıflar için özyinelemeli olması gereken başka bir özelliktir. Ve her zamanki gibi, sanal işlevlere veya sanal temel sınıflara izin verilmez. Bu, düzeni C ile uyumsuz hale getirir.

Burada rahat bir kural, standart mizanpaj sınıflarının aynı erişim kontrolüne sahip statik olmayan tüm veri üyelerine sahip olması gerektiğidir. Daha önce bunların hepsi herkese açık olmak zorundaydı , ancak şimdi hepsi özel veya hepsi korunmuş olduğu sürece onları özel veya korunmuş yapabilirsiniz .

Kalıtım kullanılırken , tüm kalıtım ağacında yalnızca bir sınıf statik olmayan veri üyelerine sahip olabilir ve ilk statik olmayan veri üyesi temel sınıf türünden (bu diğer adlandırma kurallarını kırabilir) olamaz, aksi takdirde standart düzen sınıfı.

Tanım, standart metinde şu şekildedir:

Standart mizanpaj sınıfı, aşağıdakileri yapan bir sınıftır:

- standart olmayan mizanpaj sınıfı (veya bu tür bir dizi) veya başvuru türünde statik olmayan veri üyeleri yoksa,

- sanal işlevleri (10.3) ve sanal temel sınıfları (10.1) yoktur,

- statik olmayan tüm veri üyeleri için aynı erişim kontrolüne (Madde 11) sahiptir,

- standart olmayan yerleşim temel sınıfları yoktur,

- ya en çok türetilmiş sınıfta statik olmayan veri üyeleri ve statik olmayan veri üyelerine sahip en fazla bir temel sınıf bulunmaması ya da statik olmayan veri üyelerine sahip temel sınıfların olmaması ve

- ilk statik olmayan veri elemanı ile aynı türde temel sınıflara sahip değildir.

Standart düzen yapısı, sınıf anahtarı yapısı veya sınıf anahtarı sınıfı ile tanımlanan standart düzen sınıfıdır.

Standart mizanpaj birliği, sınıf anahtarı birliği ile tanımlanan standart mizanpaj sınıfıdır.

[ Not: Standart yerleşim sınıfları, diğer programlama dillerinde yazılmış kodlarla iletişim kurmak için kullanışlıdır. Onların düzeni 9.2'de belirtilmiştir. —End not ]

Ve birkaç örnek görelim.

// empty classes have standard-layout
struct StandardLayout1 {};

struct StandardLayout2 {
    int x;
};

struct StandardLayout3 {
private: // both are private, so it's ok
    int x;
    int y;
};

struct StandardLayout4 : StandardLayout1 {
    int x;
    int y;

    void f(); // perfectly fine to have non-virtual functions
};

struct StandardLayout5 : StandardLayout1 {
    int x;
    StandardLayout1 y; // can have members of base type if they're not the first
};

struct StandardLayout6 : StandardLayout1, StandardLayout5 {
    // can use multiple inheritance as long only
    // one class in the hierarchy has non-static data members
};

struct StandardLayout7 {
    int x;
    int y;
    StandardLayout7(int x, int y) : x(x), y(y) {} // user-provided ctors are ok
};

struct StandardLayout8 {
public:
    StandardLayout8(int x) : x(x) {} // user-provided ctors are ok
// ok to have non-static data members and other members with different access
private:
    int x;
};

struct StandardLayout9 {
    int x;
    static NonStandardLayout1 y; // no restrictions on static members
};

struct NonStandardLayout1 {
    virtual f(); // cannot have virtual functions
};

struct NonStandardLayout2 {
    NonStandardLayout1 X; // has non-standard-layout member
};

struct NonStandardLayout3 : StandardLayout1 {
    StandardLayout1 x; // first member cannot be of the same type as base
};

struct NonStandardLayout4 : StandardLayout3 {
    int z; // more than one class has non-static data members
};

struct NonStandardLayout5 : NonStandardLayout3 {}; // has a non-standard-layout base class

Sonuç

Bu yeni kurallarla artık çok daha fazla tür POD olabilir. Ve bir tür POD olmasa bile, bazı POD özelliklerinden ayrı olarak yararlanabiliriz (yalnızca önemsiz veya standart yerleşimden biriyse).

Standart kitaplığın başlıkta bu özellikleri test etmek için özellikleri vardır <type_traits>:

template <typename T>
struct std::is_pod;
template <typename T>
struct std::is_trivial;
template <typename T>
struct std::is_trivially_copyable;
template <typename T>
struct std::is_standard_layout;

2
lütfen aşağıdaki kuralları detaylandırabilir misiniz: a) standart mizanpaj sınıfları, aynı erişim kontrolüne sahip statik olmayan tüm veri üyelerine sahip olmalıdır; b) tüm miras ağacında yalnızca bir sınıf statik olmayan veri üyelerine sahip olabilir ve ilk statik olmayan veri üyesi temel sınıf tipinde olamaz (bu, diğer adlandırma kurallarını bozabilir). Özellikle onların nedenleri nelerdir? Daha sonraki kural için, kenar yumuşatmaya örnek verebilir misiniz?
Andriy Tylychko

@AndyT: Cevabımı gör. Bildiğim kadarıyla cevap vermeye çalıştım.
Nicol Bolas

5
Bunu, kümeler için "küme ayracı veya eşit başlatıcı yok" gereksinimini kaldıran C ++ 14 için güncelleştirmek isteyebilirsiniz.
TC

@TC bilgi için teşekkürler. Yakında bu değişiklikleri arayacağım ve güncelleyeceğim.
R. Martinho Fernandes

1
Diğer adlandırma ile ilgili olarak: C sınıfı bir (boş) taban X'e sahipse ve C'nin ilk veri üyesi X tipi ise, bu ilk üyenin X tabanıyla aynı konumda olamayacağı bir C ++ düzen kuralı vardır; Bundan kaçınmak gerekirse, önünden kukla bir dolgu baytı alır. Aynı adreste iki X (veya alt sınıf) örneğine sahip olmak, farklı örnekleri adresleri aracılığıyla ayırt etmek için gerekli olan şeyleri kırabilir (boş bir örneğin ayırt etmek için başka hiçbir şeyi yoktur ...). Her durumda, bu dolgu baytını koyma ihtiyacı 'düzen uyumlu'yu keser.
Mart'ta greggo

106

C ++ 14 için neler değişti

Referans için Taslak C ++ 14 standardına başvurabiliriz .

Agregalar

Bu, bize aşağıdaki tanımı veren 8.5.1 Agregalar bölümünde ele alınmıştır :

Toplama, kullanıcı tarafından sağlanan kurucular (12.1), özel veya korumalı statik olmayan veri üyeleri (Madde 11), temel sınıflar (Madde 10) ve sanal işlevler (10.3) bulunmayan bir dizi veya sınıftır (Madde 9). ).

Tek değişiklik şimdi sınıf içi üye başlatıcıları eklemek bir sınıfı toplu olmayan bir hale getirmiyor. Dolayısıyla, aşağıdaki örnekte hız içi başlatıcılara sahip sınıflar için C ++ 11 toplam başlatma :

struct A
{
  int a = 3;
  int b = 3;
};

C ++ 11'de bir toplu değildi, ancak C ++ 14'te. Bu değişiklik N3605 :

Bjarne Stroustrup ve Richard Smith, toplam başlatma ve üye başlatıcıların birlikte çalışmadığı konusunda bir sorun ortaya çıkardı. Bu makale, Smith'in, üye başlatıcılara sahip olamayacağı bir kısıtlamayı ortadan kaldıran Smith'in önerdiği ifadeyi benimseyerek sorunu gidermeyi önermektedir.

POD aynı kalır

POD ( düz eski veriler ) yapısının tanımı, 9 Sınıflar bölümünde anlatılmıştır :

POD yapısı 110 , hem önemsiz bir sınıf hem de standart mizanpaj sınıfı olan ve POD olmayan yapı, POD olmayan birleşme (veya bu tür bir dizi) türünde statik olmayan veri üyelerine sahip olmayan birleşik sınıftır. Benzer şekilde, bir POD birliği hem önemsiz bir sınıf hem de standart mizanpaj sınıfı olan ve POD olmayan yapı, POD olmayan birleşme (veya bu tür bir dizi) türünde statik olmayan veri üyelerine sahip olmayan bir birliktir. POD sınıfı, POD yapısı veya POD birleşimi olan bir sınıftır.

C ++ 11 ile aynı ifadedir.

C ++ 14 için Standart Düzen Değişiklikleri

Yorumlarda belirtildiği gibi, pod standart yerleşimin tanımına dayanmaktadır ve bu C ++ 14 için değişmiştir, ancak bu durumdan sonra C ++ 14'e uygulanan hata raporları yoluyla olmuştur.

Üç DR vardı:

Yani standart düzen bu C ++ 14'ten geçti:

Standart mizanpaj sınıfı, aşağıdakileri yapan bir sınıftır:

  • (7.1) standart olmayan mizanpaj sınıfı (veya bu tür bir dizi) veya başvuru türünde statik olmayan veri üyeleri yoksa,
  • (7.2) sanal işlevleri ([class.virtual]) ve sanal temel sınıfları ([class.mi]) yoktur,
  • (7.3) statik olmayan tüm veri üyeleri için aynı erişim kontrolüne (Madde [class.access]) sahiptir,
  • (7.4) standart olmayan yerleşim temel sınıfları yoktur,
  • (7.5) ya en çok türetilmiş sınıfta statik olmayan veri üyeleri ve statik olmayan veri üyelerine sahip en fazla bir temel sınıf yoktur ya da statik olmayan veri üyelerine sahip temel sınıfları yoktur ve
  • (7.6) birinci statik olmayan veri elemanı ile aynı tipte temel sınıflara sahip değildir.

Buna C ++ 14'te :

S sınıfı, aşağıdaki durumlarda standart mizanpaj sınıfıdır:

  • (3.1) standart olmayan mizanpaj sınıfı (veya bu tür bir dizi) veya başvuru türünde statik olmayan veri üyeleri yoksa,
  • (3.2) sanal işlevleri ve sanal temel sınıfları yoktur,
  • (3.3) statik olmayan tüm veri üyeleri için aynı erişim kontrolüne sahiptir,
  • (3.4) standart olmayan yerleşim temel sınıflarına sahip değildir,
  • (3.5) herhangi bir türden en fazla bir temel sınıf alt nesneye sahip olması,
  • (3.6) sınıftaki tüm statik olmayan veri üyelerine ve bit alanlarına ve ilk olarak aynı sınıfta bildirilen temel sınıflara sahiptir ve
  • (3.7), baz sınıfı olarak M (S) tipi kümelerin hiçbir elemanına sahip değildir; burada X, M (X) türleri için aşağıdaki gibi tanımlanır. 104 [Not: M (X), X'de sıfır ofsetinde olabilecek taban sınıfı olmayan tüm alt nesneler. - son not]
    • (3.7.1) X, statik olmayan veri üyesi olmayan (muhtemelen devralınan) sendikasız sınıf tipindeyse, M (X) kümesi boştur.
    • (3.7.2) X, sıfır boyutta olan veya X'in ilk statik olmayan veri üyesi olan X0 türünde statik olmayan bir veri üyesi olan birleşik olmayan sınıf tipiyse (burada adı geçen üye anonim bir birleşim olabilir) ), M (X) grubu X0 ve M (X0) elemanlarından oluşur.
    • (3.7.3) X bir birleşim tipiyse, M (X) kümesi tüm M (Ui) ve tüm Ui'leri içeren kümedir, burada her Ui X'in statik olmayan veri elemanının tipidir. .
    • (3.7.4) X, Xe eleman tipine sahip bir dizi tipiyse, M (X) grubu Xe ve M (Xe) elemanlarından oluşur.
    • (3.7.5) X, sınıf olmayan, dizi olmayan bir türse, M (X) kümesi boştur.

4
Agregaların varsayılan olarak inşa edilebilir olduğu sürece bir temel sınıfa izin vermeleri için bir öneri vardır, bkz. N4404
Shafik Yaghmour

POD aynı kalabilirken, POD için bir gereksinim olan C ++ 14 StandardLayoutType, cppref'e göre değişti: en.cppreference.com/w/cpp/named_req/StandardLayoutType
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

1
@CiroSantilli 新疆 改造 中心 六四 事件 法轮功 teşekkür ederim, bunları nasıl özlediğimi bilmiyorum, önümüzdeki birkaç gün içinde güncellemeye çalışacağım.
Shafik Yaghmour

C ++ 14'te POD olan ancak C ++ 11'de olmayan bir örnek bulabiliyorsanız bana bildirin :-) stackoverflow.com/questions/146452/what-
-pod

1
Burada olan şey C ++ 11 ve C ++ 14'teki standart mizanpaj açıklamasına bakarsak . Bu değişiklikler, hata raporları ile uygulandığında C ++ 14'e geri döner. Yani bunu ilk başta yazdığım zaman doğruydu :-p
Shafik Yaghmour

47

lütfen aşağıdaki kuralları hazırlayabilir misiniz?

Deneyeceğim:

a) standart mizanpaj sınıfları, aynı erişim kontrolüne sahip statik olmayan tüm veri üyelerine sahip olmalıdır

Bu basit: tüm statik olmayan veri üyeleri olmalıdır hepsi olabilir public, privateya da protected. Bazılarına publicve bazılarına sahip olamazsın private.

Onlar için akıl yürütme, "standart mizanpaj" ile "standart mizanpaj değil" arasında bir ayrım yapma gerekçesine dayanır. Yani, derleyiciye işleri nasıl hafızaya alabileceğini seçme özgürlüğü vermek. Bu sadece kararlı işaretçilerle ilgili değil.

98'de C ++ standardize ettiklerinde, insanların bunu nasıl uygulayacağını tahmin etmek zorundaydılar. C ++ 'ın çeşitli lezzetleri ile uygulama deneyimi biraz olsa da, konulardan emin değildiler. Bu yüzden temkinli olmaya karar verdiler: derleyicilere mümkün olduğunca çok özgürlük verin.

Bu yüzden C ++ 98'deki POD'un tanımı çok katıdır. Çoğu sınıf için üye düzeninde C ++ derleyicilerine büyük enlem verdi. Temel olarak, POD tiplerinin özel durumlar olması amaçlanmıştır, bir nedenden dolayı özellikle yazdığınız bir şey.

C ++ 11 üzerinde çalışırken derleyicilerle çok daha fazla deneyimleri vardı. Ve ... C ++ derleyici yazarlarının gerçekten tembel olduğunu fark ettiler. Hepsi bu özgürlüğü vardı, ama onlar yoktu yapmak onunla her şeyi.

Standart mizanpaj kuralları az çok kodlayıcı ortak uygulamadır: çoğu derleyici bunları uygulamak için herhangi bir şey olsa bile çok fazla değişmek zorunda değildi (belki de ilgili tip özellikleri için bazı şeyler dışında).

O geldiğinde Şimdi, public/ private, durum farklı. Hangi üyelerin publicvs. karşı olduklarını yeniden sıralama özgürlüğü private, özellikle hata ayıklama yapılarında derleyici için önemli olabilir. Standart mizanpajın amacı diğer dillerle uyumluluk olduğu için, hata ayıklama ve sürümde mizanpajın farklı olmasını sağlayamazsınız.

Sonra kullanıcıya gerçekten zarar vermediği gerçeği var. Kapsüllenmiş bir sınıf oluşturuyorsanız, tüm veri üyelerinizin privateyine de olma ihtimali yüksektir. Genel veri üyelerini genellikle tamamen kapsüllenmiş türlerde göstermezsiniz. Bu sadece bunu yapmak isteyen, bu bölümü isteyen birkaç kullanıcı için bir problem olacaktır.

Yani büyük bir kayıp değil.

b) tüm miras ağacında sadece bir sınıf statik olmayan veri üyelerine sahip olabilir,

Bunun nedeni, standart düzeni neden tekrar standartlaştırdıklarına geri dönüyor: ortak uygulama.

Bir şeyleri saklayan iki miras ağacına sahip olmak söz konusu olduğunda yaygın bir uygulama yoktur . Bazıları temel sınıfı türetilmeden önce, diğerleri bunu diğer şekilde yapar. Üyeleri iki temel sınıftan gelmeleri halinde hangi yoldan sipariş veriyorsunuz? Ve bunun gibi. Derleyiciler bu sorular üzerinde büyük farklılıklar gösterir.

Ayrıca, sıfır / bir / sonsuzluk kuralı sayesinde, üyelerle iki sınıfınız olabileceğini söyledikten sonra, istediğiniz kadar diyebilirsiniz. Bu, bunun üstesinden gelmek için bir çok düzen kuralı eklemeyi gerektirir. Birden fazla mirasın nasıl çalıştığını, hangi sınıfların verilerini diğer sınıfların önüne koyduğunu vb. Söylemelisiniz. Çok az maddi kazanç için bu çok kural.

Sanal işlevleri ve varsayılan bir kurucu standart düzeni olmayan her şeyi yapamazsınız.

ve statik olmayan ilk veri üyesi temel sınıf türünde olamaz (bu, diğer adlandırma kurallarını bozabilir).

Gerçekten bununla konuşamam. Gerçekten anlamak için C ++ 'ın takma kuralları konusunda yeterince eğitimli değilim. Ancak temel elemanın, temel sınıfın kendisiyle aynı adresi paylaşmasıyla bir ilgisi vardır. Yani:

struct Base {};
struct Derived : Base { Base b; };

Derived d;
static_cast<Base*>(&d) == &d.b;

Ve bu muhtemelen C ++ 'ın örtüşme kurallarına aykırıdır. Bir şekilde.

Ancak şunu düşünün: Bunu yapabilme yeteneği gerçekten ne kadar yararlı olabilir ? Yalnızca bir sınıf statik olmayan veri üyelerine sahip olabileceğinden, Derivedo sınıf olmalıdır (çünkü Basebir üyesi olduğu için). Yani Base gerekir (veri) boş. Ve eğer Baseboşsa ve bir temel sınıf ... neden bir veri üyesi var?

Yana Baseboştur, hiçbir devlet vardır. Bu nedenle, statik olmayan herhangi bir üye işlevi, thisişaretçilerine değil, parametrelerine göre yaptıklarını yapar .

Yine: büyük kayıp yok.


Açıklama için teşekkürler, çok yardımcı olur. Muhtemelen static_cast<Base*>(&d)ve &d.baynı Base*tür olmasına rağmen , farklı şeylere işaret ediyorlar ve böylece örtüşme kuralını kırıyorlar. Lütfen düzelt beni.
Andriy Tylychko

1
ve neden yalnızca bir sınıf statik olmayan veri üyelerine sahipse, o Derivedzaman bu sınıf olmalıdır?
Andriy Tylychko

3
@AndyT: Derivedİlk üyenin temel sınıfı olması için iki şeyi olmalıdır: bir temel sınıf ve bir üye . Ve hiyerarşideki yalnızca bir sınıfın üyeleri olabileceğinden (ve yine de standart mizanpajda olduğundan), temel sınıfının üye olamayacağı anlamına gelir.
Nicol Bolas

3
@AndyT, Evet, takma kural konusunda esasen haklısın IME. Farklı bellek adreslerine sahip olmak için aynı türden iki farklı örnek gerekir. (Bu, bellek adresleriyle nesne kimliği izlemeye izin verir.) Temel nesne ve ilk türetilmiş üye farklı örneklerdir, bu nedenle sınıfın düzenini etkileyen dolgu eklenmeye zorlayan farklı adreslere sahip olmalıdırlar. Farklı türden olsalardı, önemli olmazdı; farklı türdeki nesnelerin aynı adrese sahip olmasına izin verilir (örneğin bir sınıf ve ilk veri üyesi).
Adam H. Peterson

46

C ++ 17'deki değişiklikler

C ++ 17 Uluslararası Standart final taslağını buradan indirin .

Agregalar

C ++ 17, agregaları ve toplu başlatmayı genişletir ve geliştirir. Standart kütüphane artık bir std::is_aggregatetür özellik sınıfı da içermektedir . İşte bölüm 11.6.1.1 ve 11.6.1.2'deki resmi tanım (elli iç referanslar):

Toplama,
kullanıcı tarafından sağlanan, açık veya devralınan kurucuları
olmayan, özel veya korumalı statik olmayan veri üyeleri olmayan,
- sanal işlevleri
olmayan ve - sanal, özel veya korumalı temel sınıfları olmayan bir dizi veya sınıftır.
[Not: Toplam başlatma, korumalı ve özel temel sınıf üyelerine veya kurucularına erişmeye izin vermez. —End note]
Bir toplamanın öğeleri şunlardır:
- bir dizi için, artan alt simge sırasındaki dizi öğeleri veya
- bir sınıf için, bildirim düzenindeki doğrudan temel sınıflar, ardından doğrudan olmayan statik olmayan veri üyeleri anonim birliğin üyeleri, beyan düzeninde.

Ne değişti?

  1. Toplamlar artık genel, sanal olmayan temel sınıflara sahip olabilir. Ayrıca, temel sınıfların toplu olması bir gereklilik değildir. Birleştirilmiş değilse, listeye ilklendirilirler.
struct B1 // not a aggregate
{
    int i1;
    B1(int a) : i1(a) { }
};
struct B2
{
    int i2;
    B2() = default;
};
struct M // not an aggregate
{
    int m;
    M(int a) : m(a) { }
};
struct C : B1, B2
{
    int j;
    M m;
    C() = default;
};
C c { { 1 }, { 2 }, 3, { 4 } };
cout
    << "is C aggregate?: " << (std::is_aggregate<C>::value ? 'Y' : 'N')
    << " i1: " << c.i1 << " i2: " << c.i2
    << " j: " << c.j << " m.m: " << c.m.m << endl;

//stdout: is C aggregate?: Y, i1=1 i2=2 j=3 m.m=4
  1. Açık varsayılan yapılandırıcılara izin verilmiyor
struct D // not an aggregate
{
    int i = 0;
    D() = default;
    explicit D(D const&) = default;
};
  1. Devralma yapıcılarına izin verilmiyor
struct B1
{
    int i1;
    B1() : i1(0) { }
};
struct C : B1 // not an aggregate
{
    using B1::B1;
};


Önemsiz Sınıflar

Önemsiz sınıfın tanımı, C ++ 14'te ele alınmayan çeşitli kusurları ele almak için C ++ 17'de yeniden düzenlendi. Değişiklikler teknik nitelikteydi. İşte 12.0.6'daki yeni tanım (dahili referanslar elid):


Önemsiz olarak kopyalanabilen bir sınıf bir sınıftır: - her kopya oluşturucu, taşıma kurucusu, kopya atama operatörü ve taşıma ataması operatörü silinmiş veya önemsizdir
- en az bir silinmemiş kopya oluşturucu, taşıma kurucusu, kopya atama operatörü, veya atama işlecini taşıyabilir ve
- önemsiz, silinmemiş bir yıkıcıya sahip olabilir.
Önemsiz sınıf, önemsiz şekilde kopyalanabilen ve tümü önemsiz veya silinmiş ve en az biri silinmeyen bir veya daha fazla varsayılan kurucuya sahip bir sınıftır. [Not: Özellikle önemsiz bir şekilde kopyalanabilen veya önemsiz bir sınıfın sanal işlevleri veya sanal temel sınıfları yoktur. - son not]

değişiklikler:

  1. C ++ 14 altında, bir sınıfın önemsiz olması için, sınıfın önemsiz olmayan herhangi bir kopyala / taşı yapıcısı / atama işleci olamazdı. Ancak, sonradan örtülü olarak varsayılan yapıcı / işleç olarak bildirilen önemsiz olabilir ve yine de silinmiş olarak tanımlanabilir , çünkü örneğin sınıf, kopyalanamayan / taşınamayan sınıf türünde bir alt nesne içeriyordu. Bu kadar önemsiz olmayan, silinmiş olarak tanımlanan yapıcı / işleçlerin varlığı tüm sınıfın önemsiz olmasına neden olur. Yıkıcılar için de benzer bir sorun vardı. C ++ 17, bu tür kurucu / operatörlerin varlığının, sınıfın önemsiz bir şekilde kopyalanmasına, dolayısıyla önemsiz olmamasına neden olduğunu ve önemsiz derecede kopyalanabilir bir sınıfın önemsiz, silinmemiş bir yıkıcıya sahip olması gerektiğini açıklığa kavuşturmaktadır. DR1734 , DR1928
  2. C ++ 14, önemsiz derecede kopyalanabilir bir sınıfın, dolayısıyla önemsiz bir sınıfın, her kopya / taşıma yapıcısı / atama işlecinin silindi olarak bildirilmesine izin verdi. Sınıf gibi standart bir düzen de olsa, yasal olarak kopyalanabilir / taşınabilir std::memcpy. Bu semantik bir çelişkiydi, çünkü silinen tüm yapıcı / ödev operatörleri olarak tanımlandığında, sınıfın yaratıcısı sınıfın kopyalanamayacağını / taşınamayacağını açıkça hedeflemişti, ancak sınıf yine de önemsiz derecede kopyalanabilir bir sınıf tanımıyla tanıştı. Bu nedenle C ++ 17'de, önemsiz derecede kopyalanabilir sınıfın en az bir önemsiz, silinmemiş (her zaman kamuya açık olmasa da) kopyala / taşı kurucu / atama operatörü olması gerektiğini belirten yeni bir cümleye sahibiz. Bkz. N4148 , DR1734
  3. Üçüncü teknik değişiklik, varsayılan kurucularla benzer bir sorunla ilgilidir. C ++ 14 altında bir sınıf, örtük olarak silinmiş olarak tanımlanan ancak yine de önemsiz bir sınıf olan önemsiz varsayılan kuruculara sahip olabilir. Yeni tanım, önemsiz bir sınıfın en az bir önemsiz, silinmemiş varsayılan kurucuya sahip olması gerektiğini açıklığa kavuşturur. Bkz. DR1496

Standart Yerleşim Sınıfları

Standart mizanpajın tanımı da kusur raporlarını ele almak için elden geçirildi. Değişiklikler yine teknik nitelikteydi. İşte standart metin (12.0.7). Daha önce olduğu gibi, dahili referanslar seçilmiştir:

Bir S sınıfı, standart mizanpaj sınıfıdır:
- standart olmayan mizanpaj sınıfı (veya bu tür bir dizi) veya başvuru türünde statik olmayan veri üyeleri yoksa,
- sanal işlevleri ve sanal temel sınıfları yoksa,
- tüm statik olmayan veri üyeleri için aynı erişim denetimine
sahiptir , - standart olmayan düzen temel sınıflarına sahip değildir,
- herhangi bir türde en fazla bir temel sınıf alt nesneye
sahiptir , - tüm statik olmayan veri üyelerine ve bit alanlarına sahiptir. sınıf ve temel sınıfları ilk önce aynı sınıfta bildirilir ve
- temel sınıf olarak (aşağıda tanımlanan) tip M (S) kümesinin hiçbir öğesi yoktur. 108
M (X) aşağıdaki gibi tanımlanır:
- X, statik olmayan veri üyesi olmayan (muhtemelen devralınan) bir birleşim olmayan sınıf tipiyse, M (X) kümesi boştur.
- X, ilk statik olmayan veri üyesi X0 tipinde (adı geçen üye anonim bir birleşim olabilir) bir birleşim dışı sınıf tipiyse, M (X) kümesi X0 ve M (X0) öğelerinden oluşur.
- X bir birleşim tipiyse, M (X) kümesi tüm M (Ui) ve tüm Ui'leri içeren kümedir, burada her Ui X'in statik olmayan veri elemanının tipidir.
- X Xe eleman tipine sahip bir dizi tipidir, M (X) grubu Xe ve M (Xe) elemanlarından oluşur.
- X, sınıf olmayan, dizi olmayan bir türse, M (X) kümesi boştur.
[Not: M (X), standart mizanpaj sınıfında X'de sıfır ofsetinde olması garanti edilen, taban sınıfı olmayan tüm alt nesne türlerinin kümesidir. — Not notu]
[Örnek:

struct B { int i; }; // standard-layout class
struct C : B { }; // standard-layout class
struct D : C { }; // standard-layout class
struct E : D { char : 4; }; // not a standard-layout class
struct Q {};
struct S : Q { };
struct T : Q { };
struct U : S, T { }; // not a standard-layout class
—End örnek]
108) Bu, aynı sınıf türüne sahip ve aynı türetilmiş aynı nesneye ait iki alt nesnenin aynı adreste tahsis edilmemesini sağlar.

değişiklikler:

  1. Türev ağacında yalnızca bir sınıfın "statik olmayan veri üyelerine sahip olması gereksiniminin, bu tür veri üyelerinin ilk olarak bildirildiği, miras alınabilecekleri sınıflara değil, bu gereksinimi statik olmayan bit alanlarına genişlettiği bir sınıfa yönelik olduğu açıklığa kavuşturuldu . Ayrıca, standart düzen sınıfının "herhangi bir türden en fazla bir temel sınıf alt nesnesi" olduğu açıklığa kavuşturuldu. Bkz. DR1813 , DR1881
  2. Standart mizanpajın tanımı hiçbir temel sınıf türünün ilk statik olmayan veri elemanı ile aynı tipte olmasına izin vermedi. Ofset sıfırdaki bir veri üyesinin herhangi bir temel sınıfla aynı türe sahip olduğu bir durumdan kaçınmaktır. C ++ 17 standardı, "bu tür türleri yasaklamak için" standart düzen sınıfında sıfırdan uzak olması garanti edilen taban sınıfı olmayan tüm alt nesne türlerinin kümesi "nin daha titiz ve özyinelemeli bir tanımını sağlar. herhangi bir temel sınıfın türü olmaktan Bkz. DR1672 , DR2120 .

Not: C ++ standartlar komitesi, yeni dil yayınlanan C ++ 14 standardında olmasa da, yukarıdaki değişikliklerin hata raporlarına dayalı olarak C ++ 14'e uygulanmasını amaçlamıştır. C ++ 17 standardındadır.


Not Cevabımı güncelledim , standart düzen değişiklikleri kusurlarının CD4 durumu var, bu da aslında C ++ 14'e uygulandığı anlamına geliyor. Bu yüzden cevabım b / c'yi içermiyordu, bu cevabımı yazdıktan sonra oldu.
Shafik Yaghmour

Not, bu soruya bir ödül kazandım.
Shafik Yaghmour

Teşekkürler @ShafikYaghmour. Kusur raporlarının durumunu gözden geçireceğim ve cevabımı buna göre değiştireceğim.
ThomasMcLeod

@ShafikYaghmour, C ++ 14 sürecinin gözden geçirilmesinden sonra ve bana bu DR'lerin Haziran 2014'te "kabul edildiğini" söylerken, Rapperswil, Şubat 2014'teki Issaquah toplantısının dilini toplantıda C ++ 14 oldu. Bkz. İsocpp.org/blog/2014/07/trip-report-summer-iso-c-meeting "ISO kurallarına göre C ++ çalışma belgesinde yapılan düzenlemeleri resmi olarak onaylamadık." Bir şey mi kaçırıyorum?
ThomasMcLeod

'CD4' statüsüne sahiptirler, yani C ++ 14 modunda başvurmaları gerekir.
Shafik Yaghmour

14

Ne değişir?

Bu sorunun açık temasının ardından, agregaların anlamı ve kullanımı her standartla değişmeye devam ediyor. Ufukta birkaç önemli değişiklik var.

Kullanıcı tarafından bildirilen kurucular P1008 ile türleri

C ++ 17'de bu tür hala bir toplamdır:

struct X {
    X() = delete;
};

Ve bu nedenle, X{}hala derler çünkü bu, bir yapıcı çağrısı değil, toplam başlatmadır. Ayrıca bkz: Özel kurucu ne zaman özel kurucu değildir?

C ++ 20'de, kısıtlama aşağıdakilerden farklı olacak:

kullanıcı tarafından sağlanan explicitveya devralınan kurucu yok

için

kullanıcı tarafından bildirilmiş veya devralınan kurucu yok

Bu, C ++ 20 çalışma taslağına uyarlanmıştır . Ne Xburada ne de Cbağlantılı soruda C ++ 20'de toplanacaklar.

Bu ayrıca aşağıdaki örnekle yo-yo efekti sağlar:

class A { protected: A() { }; };
struct B : A { B() = default; };
auto x = B{};

C ++ / 14 11, Bolduğu değil nedeniyle temel sınıf bir agrega böylece B{}gerçekleştirir değer başlatma Aramaların B::B()Aramaların A::A(), erişilebilir olduğu bir noktada. Bu iyi biçimlenmişti.

C ++ 17'de, Btoplam sınıflandırmalara izin verilen temel sınıflara izin verildiğinden toplu hale geldi B{}. Bu, kopya listesinin başlatılmasını bir Afrom'den {}, ancak Berişilemediği bağlamın dışından gerektirir . C ++ 17'de, bu kötü biçimlidir ( auto x = B();yine de iyi olurdu).

Şimdi C ++ 20'de, yukarıdaki kural değişikliği nedeniyle, bir Bkez daha (temel sınıf nedeniyle değil, ancak kullanıcı tarafından bildirilen varsayılan kurucu nedeniyle - varsayılan olsa da) bir toplam olmaktan çıkar. Bu yüzden Byapıcısına geri dönüyoruz ve bu pasaj iyi biçimlenmiş hale geliyor.

Parantezleri parantez içine alınmış değerler listesinden başlatma P960

Ortaya çıkan yaygın bir sorun, emplace()agregalara sahip-stil yapıcıları kullanmak istemektir :

struct X { int a, b; };
std::vector<X> xs;
xs.emplace_back(1, 2); // error

Bu işe yaramaz, çünkü geçerli olmayan emplacebaşlatmayı etkili bir şekilde gerçekleştirmeye çalışacaktır X(1, 2). Tipik çözüm bir kurucu eklemektir X, ancak bu öneriyle (şu anda Çekirdek yoluyla çalışmaktadır), agregalar doğru şeyi yapan ve düzenli kurucular gibi davranan sentezlenmiş kuruculara etkili bir şekilde sahip olacaktır. Yukarıdaki kod C ++ 20'deki gibi derlenecektir.

Sınıf Şablon değişken kesintinin agregalar için (CTAD) p1021 (özellikle P1816 )

C ++ 17'de bu derlenmez:

template <typename T>
struct Point {
    T x, y;
};

Point p{1, 2}; // error

Kullanıcıların tüm toplu şablonlar için kendi kesinti kılavuzlarını yazmaları gerekir:

template <typename T> Point(T, T) -> Point<T>;

Ancak bu bir bakıma yapılacak "bariz şey" olduğundan ve temelde sadece ortak özellik olduğundan, dil bunu sizin için yapacaktır. Bu örnek C ++ 20'de derlenecektir (kullanıcı tarafından sağlanan kesinti kılavuzuna gerek olmadan).


Ben upvote olacak olsa da, bunu eklemek için biraz erken hissediyorum, C ++ 2x olsa yapılmadan önce bu değişecek büyük bir şey bilmiyorum.
Shafik Yaghmour

@ShafikYaghmour Evet, muhtemelen çok erken. Ancak SD'nin yeni dil özellikleri için son tarih olduğu düşünüldüğünde, bunlar farkında olduğum tek uçuş sırasında - bu bölümlerden birini daha sonra silebilir miyim? Soruyu lütufla aktif gördüm ve unutmadan önce girmenin iyi bir zaman olduğunu düşündüm.
Barry

Anlıyorum, benzer davalar için birkaç kez cazip oldum. Her zaman büyük bir şeyin değişeceğinden endişeleniyorum ve sonunda yeniden yazmak zorunda kalacağım.
Shafik Yaghmour

@ShafikYaghmour Burada hiçbir şey değişmeyecek gibi görünüyor :)
Barry

Umarım bu şimdi güncellenir, C ++ 20 zaten yayınlandı
Noone AtAll
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.