集合体とPODとは何ですか?また、それらはどのように/なぜ特別なのですか?


548

このFAQは、アグリゲートとPODに関するもので、次の内容をカバーしています。

  • 集合体とは何ですか?
  • 何でPOD S(プレーン古いデータ)?
  • それらはどのように関連していますか?
  • どのようにそしてなぜ彼らは特別なのですか?
  • C ++ 11の変更点


これらの定義の背後にある動機はおおよそであると言えるでしょうか:POD == memcpy'able、Aggregate == aggregate-initializable?
Ofek Shilon

回答:


572

読み方:

この記事はかなり長いです。集約とPOD(プレーンな古いデータ)の両方について知りたい場合は、時間をかけて読んでください。集計だけに関心がある場合は、最初の部分のみを読んでください。PODのみに関心がある場合は、最初に集計の定義、影響、および例を読む必要があります。その後、PODにジャンプできます、最初の部分全体を読むことをお勧めします。集約の概念は、PODを定義するために不可欠です。エラー(文法、文体、書式、構文などを含む)が見つかった場合は、コメントを残してください。編集します。

この回答はC ++ 03に適用されます。他のC ++標準については、以下を参照してください。

集合体とは何か、なぜそれらが特別なのか

C ++標準からの正式な定義(C ++ 03 8.5.1§1

集約は、ユーザー宣言コンストラクター(12.1)、プライベートまたは保護された非静的データメンバー(句11)、基本クラス(句10)、仮想関数(10.3)のない配列またはクラス(句9)です。 )。

それでは、この定義を解析してみましょう。まず、どの配列も集合体です。次の場合、クラスは集合体になることもできます。構造体や共用体については何も言われていません。それらは集合体になりませんか?はい、できます。C ++では、この用語classはすべてのクラス、構造体、および共用体を指します。したがって、クラス(または構造体、共用体)は、上記の定義の基準を満たす場合にのみ、集合体になります。これらの基準は何を意味しますか?

  • これは、集約クラスがコンストラクターを持つことができないことを意味しません。実際には、ユーザーによって明示的にではなく、コンパイラーによって暗黙的に宣言されている限り、デフォルトのコンストラクターやコピーコンストラクターを持つことができます。

  • プライベートまたは保護された非静的データメンバーはありません。プライベートクラスおよび保護されたメンバー関数(コンストラクターを除く)だけでなく、プライベートクラスまたは保護された静的データメンバーおよびメンバー関数も、必要に応じていくつでも持つことができ、集約クラスのルールに違反しません。

  • 集約クラスには、ユーザー宣言/ユーザー定義のコピー割り当て演算子やデストラクタを含めることができます

  • 配列は、非集合クラス型の配列であっても集合です。

次に、いくつかの例を見てみましょう。

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
};

あなたはアイデアを得ます。次に、集計が特別な方法を見てみましょう。非集計クラスとは異なり、中括弧で初期化できます{}。この初期化構文は配列でよく知られていますが、これらは集約であることを学びました。それでは、それらから始めましょう。

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

if(m == n)配列の
i番目の要素はa iで初期化され
ますelse if(m <n)
配列の最初のm要素は 1、a 2、…、a mおよびその他のn - m要素で初期化されます可能であれば、値を初期化します(用語の説明については以下を参照してください)
else if(m> n)
コンパイラはエラー
else を発行します(これは、nがのようにまったく指定されていない場合int a[] = {1, 2, 3};
のサイズ配列(n)はmに等しいと仮定されるため、int a[] = {1, 2, 3};と同等です。int a[3] = {1, 2, 3};

スカラー型のオブジェクト(場合boolintchardouble、ポインタ、等)がある値に初期化それがで初期化されている手段0(そのタイプのfalseためbool0.0double等)。ユーザー宣言のデフォルトコンストラクターを持つクラスタイプのオブジェクトが値で初期化されると、そのデフォルトコンストラクターが呼び出されます。デフォルトのコンストラクタが暗黙的に定義されている場合、すべての非静的メンバーは再帰的に値で初期化されます。この定義は不正確で少し不正確ですが、基本的な考え方がわかるはずです。参照を値で初期化することはできません。非適切なクラスの値の初期化は、たとえば、クラスに適切なデフォルトのコンストラクタがない場合に失敗する可能性があります。

配列の初期化の例:

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
}

ここで、中括弧で集約クラスを初期化する方法を見てみましょう。ほぼ同じです。配列要素の代わりに、クラス定義での出現順に非静的データメンバーを初期化します(定義によりすべてパブリックです)。メンバーよりも少ない初期化子がある場合、残りは値で初期化されます。明示的に初期化されていないメンバーの1つを値初期化することが不可能な場合は、コンパイル時エラーが発生します。イニシャライザが必要以上に多い場合、コンパイル時エラーも発生します。

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}};

上記の例でy.c用いて初期化される'a'y.x.i110y.x.i220y.i[0]20y.i[1]30y.f値初期化、で初期化されます0.0。保護された静的メンバーdはなので、まったく初期化されませんstatic

集計ユニオンは、最初のメンバーのみを中括弧で初期化できるという点で異なります。ユニオンの使用を検討するのに十分な高度なC ++を使用している場合(その使用は非常に危険であり、慎重に検討する必要があります)、標準でユニオンのルールを調べることができると思います:)。

集計の何が特別なのかがわかったところで、クラスの制限を理解してみましょう。つまり、なぜ彼らがそこにいるのか。中括弧を使用したメンバーごとの初期化は、クラスがそのメンバーの合計に過ぎないことを意味することを理解する必要があります。ユーザー定義コンストラクタが存在する場合は、ユーザーがメンバーを初期化するために追加の作業を行う必要があるため、ブレースの初期化は正しくありません。仮想関数が存在する場合、これは、このクラスのオブジェクトが(ほとんどの実装で)コンストラクターで設定されるクラスのいわゆるvtableへのポインターを持っていることを意味するため、ブレース初期化では不十分です。残りの制限は、演習と同様の方法で理解できます。

集計については十分です。これで、より厳密なタイプのセット、つまりPODを定義できます

PODとは何か、なぜPODが特別なのか

C ++標準からの正式な定義(C ++ 03 9§4

POD構造体は、非POD構造体、非PODユニオン(またはそのような型の配列)、または参照のタイプの非静的データメンバーがなく、ユーザー定義のコピー割り当て演算子とユーザー定義のデストラクタ。同様に、PODユニオンは、非POD構造体、非PODユニオン(またはそのようなタイプの配列)、または参照のタイプの非静的データメンバーがなく、ユーザー定義のコピー代入演算子がない集約ユニオンです。ユーザー定義のデストラクタはありません。PODクラスは、POD構造体またはPODユニオンのいずれかであるクラスです。

これは解析が難しいですね。:)ユニオンを(上記と同じ理由で)除外し、少し明確に言い換えましょう。

集約クラスは、ユーザー定義のコピー割り当て演算子とデストラクタがなく、非静的メンバーが非PODクラス、非PODの配列、または参照でない場合、PODと呼ばれます。

この定義は何を意味しますか?(私はPODPlain Old Dataの略であることを言及しましたか?)

  • すべてのPODクラスは集合体です。逆に言えば、クラスが集合体でない場合は、PODではありません。
  • クラスは、構造体と同様に、標準的な用語がどちらの場合もPOD構造体であっても、PODになることができます。
  • 集計の場合と同様に、クラスがどの静的メンバーを持っているかは関係ありません

例:

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クラス、PODユニオン、スカラー型、およびそのような型の配列は、まとめてPODタイプと呼ばれます。
PODは多くの点で特別です。いくつか例を挙げます。

  • PODクラスはCの構造体に最も近いものです。それらとは異なり、PODはメンバー関数と任意の静的メンバーを持つことができますが、これら2つのどちらもオブジェクトのメモリレイアウトを変更しません。したがって、Cや.NETからも使用できる多かれ少なかれ移植可能な動的ライブラリを作成したい場合は、エクスポートされたすべての関数がPOD型のパラメータのみを取得して返すようにする必要があります。

  • 非PODクラスタイプのオブジェクトの存続期間は、コンストラクタが終了したときに始まり、デストラクタが終了したときに終了します。PODクラスの場合、存続期間はオブジェクトのストレージが占有されると始まり、そのストレージが解放または再利用されると終了します。

  • PODタイプのオブジェクトの場合、標準によって、memcpyオブジェクトのコンテンツをcharまたはunsigned charの配列にしてからコンテンツをオブジェクトにmemcpy戻すと、オブジェクトは元の値を保持することが保証されます。非PODタイプのオブジェクトについては、そのような保証がないことに注意してください。また、を使用してPODオブジェクトを安全にコピーできますmemcpy。次の例では、TがPODタイプであると想定しています。

    #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
  • gotoステートメント。ご存知かもしれませんが、変数がまだスコープ内にないポイントから、すでにスコープ内にあるポイントにgotoを介してジャンプすることは違法です(コンパイラーはエラーを発行する必要があります)。この制限は、変数が非PODタイプの場合にのみ適用されます。次の例ではf()は、g()整形式ですが、整形式です。Microsoftのコンパイラはこのルールに寛大すぎることに注意してください。両方の場合に警告を発行するだけです。

    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;
    }
  • PODオブジェクトの先頭にはパディングがないことが保証されています。つまり、PODクラスAの最初のメンバーがタイプTの場合、安全にreinterpret_castからA*T*第1部材およびその逆へのポインタを取得します。

リストはどんどん続きます…

結論

ご覧のように、多くの言語機能は動作が異なるため、PODが正確に何であるかを理解することが重要です。


3
いい答えだ。コメント:「デフォルトのコンストラクタが暗黙的に定義されている場合、すべての非静的メンバーは再帰的に値で初期化されます。」また、「たとえば、クラスに適切なデフォルトコンストラクタがない場合、非集計クラスの値の初期化は失敗する可能性があります。」不正解:暗黙的に宣言されたデフォルトコンストラクターを使用したクラスの値の初期化では、暗黙的に定義されたデフォルトコンストラクターは必要ありません。このように、(挿入与えprivate:適宜):struct A { int const a; };その後A()も、整形式でAのデフォルトコンストラクタの定義が病気に形成されるだろう。
ヨハネスシャウブ-litb 2010年

4
@Kev:同じ情報を短い回答にまとめることができれば、喜んで投票します!
sbi 2010年

3
@Armenは、同じ質問に対して複数の回答を行うことができることにも注意してください。各回答には、質問に対する解決策の一部を含めることができます。私の意見では、マーク付きのものを受け入れたネジ:)
Johannes Schaub-litb

3
答えは素晴らしいです。私はまだしばらくこの記事を再訪しています。ちなみにVisual Studioの警告について。ポッドの「gotoステートメント」は、あなたが言ったようにMSVCコンパイラを知らないようになっています。しかし、switch / caseステートメントの場合、コンパイルエラーが発生します。それに基づいて、私はいくつかのテスト・ポッド・チェッカー作った概念:stackoverflow.com/questions/12232766/test-for-pod-ness-in-c-c11/...を
bruziuz

2
「非PODクラスタイプのオブジェクトの存続期間は、コンストラクターが完了したときに始まり、デストラクターが完了したときに終了します。」で始まる箇条書きで 最後の部分は、代わりに「デストラクタが開始したとき」と言う必要があります。
Quokka 2017

457

C ++ 11の変更点

集計

集計の標準的な定義は少し変更されましたが、それでもほとんど同じです。

集約は、ユーザー提供のコンストラクター(12.1)、非静的データメンバーのブレースまたはイコライザー(9.2)、プライベートまたは保護された非静的データメンバー(条項11)、基本クラス(条項10)、仮想関数(10.3)なし。

何が変わったの?

  1. 以前は、ユーザー定義のコンストラクターを集約に含めることはできませんでしたが、ユーザー提供のコンストラクターを含めることはできなくなりました。違いはありますか?はい、あります。これで、コンストラクタを宣言してデフォルトにすることができるからです

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

    最初の宣言でデフォルトになっているコンストラクター(または任意の特別なメンバー関数)ユーザー提供ではないため、これは依然として集約です。

  2. 現在、集約では、非静的データメンバーのブレースまたはイニシャライザを使用できません。これは何を意味するのでしょうか?これは、この新しい標準を使用すると、次のようにクラスのメンバーを直接初期化できるからです。

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

    この機能を使用すると、基本的に独自のデフォルトコンストラクターを提供することと同じになるため、クラスは集合体ではなくなります。

つまり、集約とは何もほとんど変わりませんでした。新しい機能に合わせて、それはまだ基本的な考え方は同じです。

PODについてはどうですか?

PODには多くの変更が加えられました。この新しい規格では、PODに関するこれまでの多くのルールが緩和され、規格での定義の提供方法が根本的に変更されました。

PODの考え方は、基本的に2つの異なるプロパティをキャプチャすることです。

  1. 静的初期化をサポートし、
  2. C ++でPODをコンパイルすると、Cでコンパイルされた構造体と同じメモリレイアウトが得られます。

このため、定義は2つの異なる概念に分割されています。それは、PODよりも有用であるため、トリビアルクラスと標準レイアウトクラスです。現在、標準はPODという用語をほとんど使用せず、より具体的で平凡標準レイアウトを優先しています。概念がます。

新しい定義は基本的に、PODは簡単で標準的なレイアウトを持つクラスであり、このプロパティはすべての非静的データメンバーに対して再帰的に保持する必要があることを示しています。

POD構造体は、自明なクラスであり標準レイアウトクラスでもある非ユニオンクラスであり、非POD構造体、非PODユニオン(またはそのようなタイプの配列)タイプの非静的データメンバーはありません。同様に、POD共用体は、自明なクラスと標準レイアウトクラスの両方である共用体であり、非POD構造体、非POD共用体(またはそのような型の配列)型の非静的データメンバーはありません。PODクラスは、POD構造体またはPODユニオンであるクラスです。

これらの2つのプロパティを個別に詳しく見ていきましょう。

ささいなクラス

トリビアルは、上記の最初のプロパティです。トリビアルクラスは、静的な初期化をサポートしています。クラスが自明にコピー可能(自明なクラスのスーパーセット)である場合、その表現を次のように場所にコピーすることは問題ありません。memcpy、結果が同じになることを期待できます。

標準では、次のように簡単なクラスを定義しています。

自明にコピー可能なクラスは、次のようなクラスです。

—重要なコピーコンストラクターがない(12.8)。

—重要な移動コンストラクタはありません(12.8)。

—重要なコピー割り当て演算子がない(13.5.3、12.8)、

—重要な移動代入演算子(13.5.3、12.8)がない、および

—ささいなデストラクタがあります(12.4)。

自明なクラスとは、自明なデフォルトコンストラクター(12.1)を持ち、自明にコピー可能なクラスです。

[ 注:特に、自明にコピー可能なクラスまたは自明なクラスには、仮想関数または仮想基本クラスがありません。—エンドノート ]

では、これらすべての些細なこととそうでないことは何ですか?

クラスXのコピー/移動コンストラクターは、ユーザー指定ではなく、

—クラスXには仮想関数(10.3)および仮想基本クラス(10.1)がありません。

—各直接基本クラスサブオブジェクトをコピー/移動するために選択されたコンストラクタは簡単です。

—クラス型(またはその配列)であるXの非静的データメンバーごとに、そのメンバーをコピー/移動するために選択されたコンストラクターは簡単です。

それ以外の場合、コピー/移動コンストラクタは重要です。

基本的に、これは、ユーザーが指定せず、クラスに仮想的なものがない場合、コピーまたは移動コンストラクターは取るに足らないものであり、このプロパティはクラスのすべてのメンバーと基本クラスに対して再帰的に保持されることを意味します。

自明なコピー/移動代入演算子の定義は非常によく似ており、単に「コンストラクタ」という単語を「代入演算子」に置き換えます。

自明なデストラクタにも同様の定義があり、仮想にはできないという制約が追加されています。

さらに、単純なデフォルトコンストラクターにも同様のルールが存在します。加えて、クラスに上記のbrace-or-equal-initializersを持つ非静的データメンバーがある場合、デフォルトコンストラクターは重要です。

以下に、すべてをクリアする例をいくつか示します。

// 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
};

標準レイアウト

標準レイアウトは2番目のプロパティです。標準は、これらが他の言語との通信に役立つと述べています。これは、標準レイアウトクラスが、同等のC構造体または共用体と同じメモリレイアウトを持っているためです。

これは、メンバーとすべての基本クラスに対して再帰的に保持する必要がある別のプロパティです。そしていつものように、仮想関数や仮想基本クラスは許可されていません。レイアウトがCと互換性がなくなります。

ここでの緩和されたルールは、標準レイアウトクラスは、同じアクセス制御を持つすべての非静的データメンバーを持たなければならないということです。以前はすべてパブリックにする必要がありましたが、すべてプライベートまたはすべて保護されている限り、プライベートまたは保護にすることができます。

継承を使用する場合、1つだけ継承ツリー全体でクラスが非静的データメンバーを持つことができ、最初の非静的データメンバーを基本クラスタイプにすることはできません(エイリアスルールに違反する可能性があります)。それ以外の場合、標準ではありません-レイアウトクラス。

これは標準テキストでの定義の仕方です:

標準レイアウトクラスは、次のようなクラスです。

—非標準レイアウトクラス(またはそのようなタイプの配列)または参照のタイプの非静的データメンバーがありません。

—仮想関数(10.3)も仮想基本クラス(10.1)もありません。

—すべての非静的データメンバーに対して同じアクセス制御(条項11)があります。

—非標準レイアウトの基本クラスはありません。

—ほとんどの派生クラスに非静的データメンバーがなく、非静的データメンバーを持つ最大1つの基本クラスがあるか、または非静的データメンバーを持つ基本クラスがない。

—最初の非静的データメンバーと同じタイプの基本クラスがありません。

標準レイアウト構造体は、クラスキー構造体またはクラスキークラスで定義された標準レイアウトクラスです。

標準レイアウト共用体は、クラスキー共用体で定義された標準レイアウトクラスです。

[ 注:標準レイアウトクラスは、他のプログラミング言語で記述されたコードとの通信に役立ちます。それらのレイアウトは9.2で指定されています。—エンドノート ]

そして、いくつかの例を見てみましょう。

// 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

結論

これらの新しいルールにより、より多くのタイプがPODになる可能性があります。また、タイプがPODでない場合でも、いくつかのPODプロパティを個別に利用できます(それが単純なレイアウトまたは標準的なレイアウトの1つだけの場合)。

標準ライブラリには、ヘッダーでこれらのプロパティをテストする特性があります<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
次のルールについて詳しく説明してください。a)標準レイアウトクラスには、同じアクセス制御を持つすべての非静的データメンバーが必要です。b)継承ツリー全体で1つのクラスのみが非静的データメンバーを持つことができ、最初の非静的データメンバーを基本クラスタイプにすることはできません(これにより、エイリアシングルールに違反する可能性があります)。特にそれらの理由は何ですか?後者のルールについて、エイリアスを解除する例を提供できますか?
Andriy Tylychko、2011

@AndyT:私の答えを見てください。私は私の知る限りのことについて答えようとしました。
Nicol Bolas、2012

5
これは、C ++ 14でこれを更新したい場合があります。これにより、アグリゲートの「brace-or-equal-initializers」の要件がなくなりました。
TC

@TCヘッドアップに感謝します。これらの変更をすぐに調べて更新します。
R.マルティーニョフェルナンデス

1
エイリアスに関して:クラスCに(空の)ベースXがあり、Cの最初のデータメンバーがタイプXである場合、最初のメンバーをベースXと同じオフセットにできないというC ++レイアウトルールがあります。それを回避する必要がある場合は、その前にダミーのパディングバイトを取得します。同じアドレスにX(またはサブクラス)の2つのインスタンスがあると、それらのアドレスを使用して異なるインスタンスを区別する必要があるものを壊す可能性があります(空のインスタンスにはそれを区別する他の何もありません...)。いずれの場合も、そのパディングバイトを挿入する必要があると、「レイアウト互換」が壊れます。
greggo 2018年

106

C ++ 14の変更点

ドラフトC ++ 14標準を参照できますを参照できます。

集計

これはセクション8.5.1 Aggregatesでカバーされており、次の定義が得られます。

集約は、ユーザー提供のコンストラクター(12.1)、プライベートまたは保護された非静的データメンバー(条項11)、基本クラス(条項10)、および仮想関数(10.3)がない配列またはクラス(条項9)です。 )。

唯一の変更は、クラス内のメンバー初期化子を追加ても、クラスが非集合にならないことです。したがって、C ++ 11の次の例は、メンバーのインプレース初期化子を持つクラスの初期化を集約します

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

C ++ 11では集計ではありませんでしたが、C ++ 14では集計です。この変更は、N3605:メンバー初期化子と集計でカバーされています。

Bjarne StroustrupとRichard Smithは、集約初期化とメンバー初期化子が一緒に機能しないという問題を提起しました。このペーパーでは、集計にメンバー初期化子を含めることができないという制限を取り除くスミスの提案された表現を採用することで、問題を修正することを提案しています。

PODは同じまま

POD(プレーン・オールド・データ)構造体の定義は、9 クラスのセクションで説明されています。

POD構造体110は、自明なクラスであり標準レイアウトクラスでもある非ユニオンクラスであり、非POD構造体、非PODユニオン(またはそのようなタイプの配列)タイプの非静的データメンバーを持ちません。同様に、PODユニオンは、自明なクラスと標準レイアウトクラスの両方であるユニオンであり、非POD構造体、非PODユニオン(またはそのようなタイプの配列)型の非静的データメンバーを持ちません。PODクラスは、POD構造体またはPODユニオンであるクラスです。

これはC ++ 11と同じ表現です。

C ++ 14の標準レイアウトの変更

コメントで述べたように、ポッド標準レイアウトの定義に依存しており、C ++ 14では変更されましたが、これは、事後にC ++ 14に適用された欠陥レポートによるものでした。

3つのDRがありました。

したがって、標準レイアウトはこのPre C ++ 14からのものです。

標準レイアウトクラスは、次のようなクラスです。

  • (7.1)非標準レイアウトクラス(またはそのようなタイプの配列)または参照のタイプの非静的データメンバーがない。
  • (7.2)仮想関数([class.virtual])および仮想基本クラス([class.mi])がない。
  • (7.3)すべての非静的データメンバーに対して同じアクセス制御(節[class.access])がある
  • (7.4)非標準レイアウトの基本クラスはありません。
  • (7.5)最も派生したクラスに非静的データメンバーがなく、非静的データメンバーを持つ最大1つの基本クラスがあるか、非静的データメンバーを持つ基本クラスがない、および
  • (7.6)には、最初の非静的データメンバーと同じ型の基本クラスはありません。

C ++ 14で、この

次の場合、クラスSは標準レイアウトクラスです。

  • (3.1)非標準レイアウトクラス(またはそのようなタイプの配列)または参照のタイプの非静的データメンバーがない
  • (3.2)仮想関数も仮想基底クラスもありません。
  • (3.3)すべての非静的データメンバーに対して同じアクセス制御があり、
  • (3.4)非標準レイアウトの基本クラスはありません。
  • (3.5)任意のタイプの最大1つの基本クラスサブオブジェクトを持ち、
  • (3.6)同じ静的クラスで最初に宣言されたクラスとその基本クラスにすべての非静的データメンバーとビットフィールドがある
  • (3.7)には、基本クラスとしての型のセットM(S)の要素はありません。ここで、任意の型Xについて、M(X)は次のように定義されます。104[注:M(X)は、 Xのゼロオフセットにある可能性があるすべての非基本クラスのサブオブジェクト—終了ノート]
    • (3.7.1)Xが、(おそらく継承された)非静的データメンバーのない共用体クラス型である場合、セットM(X)は空です。
    • (3.7.2)Xがサイズが0であるか、Xの最初の非静的データメンバーであるX0型の非静的データメンバーを持つ非共用クラスタイプである場合(このメンバーは匿名共用体である可能性があります) )、セットM(X)はX0とM(X0)の要素で構成されます。
    • (3.7.3)Xが共用体型の場合、セットM(X)はすべてのM(Ui)とすべてのUiを含むセットの共用体であり、各UiはXのi番目の非静的データメンバーの型です。
    • (3.7.4)Xが要素タイプXeの配列タイプの場合、セットM(X)はXeとM(Xe)の要素で構成されます。
    • (3.7.5)Xが非クラス、非配列型の場合、セットM(X)は空です。

4
デフォルトで構築可能である限り、集約に基本クラスを許可する提案があります。N4404を
Shafik Yaghmour

PODは、PODのための必要条件であると同じ、C ++ 14 StandardLayoutTypeを、とどまるかもしれないが、cpprefに応じて変更されました:en.cppreference.com/w/cpp/named_req/StandardLayoutType
チロSantilli郝海东冠状病六四事件法轮功

1
@CiroSantilli新疆改造中心六四事件法轮功ありがとうございます、どうしてそれらを逃したのかわかりません。今後数日間更新を試みます。
Shafik Yaghmour

C ++ 14ではPODであるがC ++ 11ではできない例を思い付くことができるかどうかお知らせください:-)私は例の詳細なリストを始めました:stackoverflow.com/questions/146452/what- are-pod-types-in-c /…
Ciro Santilli郝海东冠状病六四事件法轮機能

1
@CiroSantilli新疆改造中心六四事件法轮功したがって、ここで何が起こったかは、C ++ 11C ++ 14の標準レイアウトの説明を一致させた場合です。これらの変更は、C ++ 14に戻る欠陥レポートを介して適用されます。だから私がこれを最初に書いたときそれは正しかった:-p
Shafik Yaghmour

47

次のルールについて詳しく説明してください。

私が試してみます:

a)標準レイアウトクラスには、同じアクセス制御を持つすべての非静的データメンバーが必要です

それは簡単です:すべての非静的データメンバをしなければならないすべてのことpublicprivateまたはprotected。あなたはいくつかpublicといくつかを持つことはできませんprivate

それらの推論は、「標準レイアウト」と「非標準レイアウト」をまったく区別するための推論になります。つまり、コンパイラに、物事をメモリに配置する方法を自由に選択できるようにします。それは、vtableポインタだけではありません。

彼らが98年にC ++を標準化したとき、彼らは基本的に人々がそれを実装する方法を予測しなければなりませんでした。C ++のさまざまなフレーバーを使用した実装の経験はかなりありましたが、確信を持っていませんでした。そこで彼らは用心することに決めました:コンパイラにできるだけ多くの自由を与えてください。

そのため、C ++ 98でのPODの定義は非常に厳密です。これにより、C ++コンパイラは、ほとんどのクラスのメンバーレイアウトに大きな自由度を与えました。基本的に、PODタイプは特別なケースであることが意図されており、あなたが理由のために具体的に書いたものです。

C ++ 11が開発されていたとき、彼らはコンパイラに関してより多くの経験を持っていました。そして彼らは、C ++コンパイラの作成者が本当に怠惰であることを理解しました。彼らはすべて、この自由を持っていたが、彼らはなかったそれで何かを。

標準レイアウトの規則は、多かれ少なかれ一般的な慣行を体系化したものです。ほとんどのコンパイラーは、それらを実装するために何かを変更する必要はほとんどありませんでした(対応する型の特性のいくつかのもの以外)。

さて、それがpublic/になるとprivate、状況は異なります。どのメンバーがpublic対であるかをprivate実際に並べ替える自由は、特にビルドのデバッグにおいて、コンパイラーにとって重要な場合があります。また、標準レイアウトのポイントは他の言語との互換性があることなので、デバッグ版とリリース版でレイアウトを変えることはできません。

そして、それがユーザーを本当に傷つけないという事実があります。カプセル化されたクラスを作成している場合、すべてのデータメンバーがいずれにしても、確率は高いprivateです。通常、完全にカプセル化された型のパブリックデータメンバーは公開しません。したがって、これは、それを実行したい、その部門を実行したい少数のユーザーにとってのみ問題になります。

したがって、大きな損失ではありません。

b)継承ツリー全体で1つのクラスのみが非静的データメンバーを持つことができます。

この理由は、標準レイアウトを再び標準化した理由に戻ります。これは一般的な方法です。

実際に物を格納する継承ツリーの2つのメンバーを持つことに関しては一般的な方法はありません。派生クラスの前に基本クラスを配置するものもあれば、逆の方法で配置するものもあります。メンバーが2つの基本クラスに属している場合、どのようにメンバーを注文しますか?等々。コンパイラはこれらの質問で大きく異なります。

また、zero / one / infinityルールのおかげで、メンバーを持つ2つのクラスを持つことができると言うと、必要なだけいくつでも言うことができます。これには、これを処理する方法に関する多くのレイアウト規則を追加する必要があります。複数の継承がどのように機能するか、どのクラスが他のクラスの前にデータを配置するかなどを言わなければなりません。これは多くのルールであり、実質的な利益はほとんどありません。

仮想関数とデフォルトのコンストラクター標準レイアウトを持たないものすべてを作成することはできません。

また、最初の非静的データメンバーを基本クラス型にすることはできません(これにより、エイリアスルールが壊れる可能性があります)。

これは本当に話せません。C ++のエイリアシングルールについて十分に理解していないため、実際にそれを理解できません。しかし、それは基本メンバーが基本クラス自体と同じアドレスを共有するという事実と関係があります。あれは:

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

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

そして、それはおそらくC ++のエイリアシング規則に反しています。何らかの方法で。

ただし、これを検討してください。これを実行する機能を実際に使用すると、どれほど役立つでしょうか。非静的データメンバーを持つことができるクラスは1つだけなので、Derivedそのクラスでなければなりません(Baseメンバーとしてがあるため)。したがって、(データの)空でBase なければなりません。そして、もしBase空の場合、基本クラスと同様に ...なぜそれのデータメンバーがあるのですか?

Baseは空なので、状態はありません。したがって、非静的メンバー関数は、thisポインターではなく、パラメーターに基づいて実行することを実行します。

繰り返しますが、大きな損失はありません。


説明してくれてありがとう、それは非常に役立ちます。おそらくにもかかわらずstatic_cast<Base*>(&d)&d.b同じBase*タイプ、彼らはこのようにエイリアシング規則を破る異なるものを指します。訂正してください。
Andriy Tylychko 2012

1
そして、なぜ1つのクラスだけが非静的データメンバーを持つことができる場合、Derivedそのクラスでなければならないのですか?
Andriy Tylychko

3
@AndyT:Derivedの最初のメンバーがその基本クラスになるためには、基本クラスとメンバーの 2つの要素が必要です。また、階層内の1つのクラスだけがメンバーを持つことができる(それでも標準レイアウトである)ため、これはその基本クラスがメンバーを持つことができないことを意味します。
Nicol Bolas

3
@AndyT、ええ、エイリアシングルールについては、IMEは基本的に正しいです。同じタイプの2つの異なるインスタンスは、異なるメモリアドレスを持つ必要があります。(これにより、メモリアドレスを使用したオブジェクトID追跡が可能になります。)ベースオブジェクトと最初の派生メンバーは異なるインスタンスであるため、それらは異なるアドレスを持つ必要があり、パディングが強制的に追加され、クラスのレイアウトに影響します。タイプが異なっていても問題ありません。異なるタイプのオブジェクトは同じアドレスを持つことができます(たとえば、クラスとその最初のデータメンバー)。
Adam H. Peterson

46

C ++ 17での変更

C ++ 17国際標準の最終ドラフトをこちらからダウンロードしてください

集計

C ++ 17は、集約および集約初期化を拡張および拡張します。標準ライブラリには、std::is_aggregateタイプ特性クラスも含まれています。セクション11.6.1.1および11.6.1.2からの正式な定義は次のとおりです(内部参照は省略されています)。

集約は、配列またはクラスであり
、ユーザー提供、明示、または継承コンストラクター
なし、プライベートまたは保護された非静的データメンバー
なし、仮想関数なし、および
仮想、プライベート、または保護された基本クラスなし。
[注:集計の初期化では、保護およびプライベートの基本クラスのメンバーまたはコンストラクターへのアクセスは許可されていません。—エンドノート]

— 集合体の要素は次のとおりです配列の場合、添え字の昇順の配列要素、または
—クラスの場合、宣言の順序の直接基本クラス、その後にない非静的データメンバー宣言の順序での匿名組合のメンバー。

何が変わったの?

  1. 集約は、パブリックな非仮想基本クラスを持つことができます。さらに、基本クラスが集合体である必要はありません。集合体でない場合は、リストで初期化されます。
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. 明示的なデフォルトのコンストラクタは許可されていません
struct D // not an aggregate
{
    int i = 0;
    D() = default;
    explicit D(D const&) = default;
};
  1. 継承コンストラクターは許可されていません
struct B1
{
    int i1;
    B1() : i1(0) { }
};
struct C : B1 // not an aggregate
{
    using B1::B1;
};


ささいなクラス

トリビアルクラスの定義がC ++ 17で作り直され、C ++ 14では対処されなかったいくつかの不具合に対処しました。変更は本質的に技術的なものでした。これは、12.0.6での新しい定義です(内部参照は省略されています)。

自明なコピー可能なクラスとは、次のクラスです。—
各コピーコンストラクター、移動コンストラクター、コピー割り当て演算子、および移動割り当て演算子が削除されるか、または自明です。—削除され
ていないコピーコンストラクター、移動コンストラクター、コピー割り当て演算子が少なくとも1つあります。または代入演算子を移動し、
—ささいな、削除されていないデストラクタがあります。
トリビアルクラスは、トリビアルコピーが可能なクラスであり、1つ以上のデフォルトコンストラクターがあり、そのすべてがトリビアルまたは削除されており、少なくとも1つは削除されていません。[注:特に、自明にコピー可能なクラスまたは自明なクラスには、仮想関数または仮想基本クラスがありません。—エンドノート]

変更:

  1. C ++ 14では、クラスが自明であるために、クラスは自明ではないコピー/移動コンストラクター/代入演算子を持つことができませんでした。ただし、デフォルトのコンストラクタ/演算子として暗黙的に宣言された場合、たとえば、クラスにコピー/移動できないクラス型のサブオブジェクトが含まれていたため、削除れたものとして定義されている可能性があります。そのような重要な削除済みとして定義されたコンストラクター/オペレーターが存在すると、クラス全体が重要なものになります。デストラクタにも同様の問題がありました。C ++ 17は、そのようなコンストラクター/演算子の存在がクラスを自明にコピーできない、つまり自明ではないこと、および自明にコピー可能なクラスには自明で削除されないデストラクタが必要であることを明確にします。DR1734DR1928
  2. C ++ 14では、自明なコピーが可能なクラス、つまり自明なクラスが、すべてのコピー/移動コンストラクター/代入演算子を削除済みとして宣言することができました。ただし、そのようなクラスも標準のレイアウトである場合は、で合法的にコピー/移動できstd::memcpyます。すべてのコンストラクター/割り当て演算子を削除済みとして定義することにより、クラスの作成者はクラスをコピー/移動できないことを明確に意図していましたが、クラスはまだ自明なコピー可能なクラスの定義を満たしていました。したがって、C ++ 17には、自明にコピー可能なクラスに、少なくとも1つの自明で削除されていない(必ずしもパブリックにアクセスできるとは限りません)コピー/移動コンストラクター/割り当て演算子が必要であるという新しい句があります。N4148DR1734を参照
  3. 3番目の技術的な変更は、デフォルトのコンストラクターに関する同様の問題に関係しています。C ++ 14では、クラスには、暗黙的に削除済みとして定義された自明なデフォルトコンストラクターがあり、それでも自明なクラスである場合があります。新しい定義では、自明なクラスには、少なくとも1つの自明な、削除されていないデフォルトコンストラクターが必要であることが明確になっています。DR1496を参照

標準レイアウトクラス

標準レイアウトの定義も、欠陥レポートに対処するために再作成されました。再び、変更は本質的に技術的なものでした。これは標準(12.0.7)からのテキストです。以前と同様に、内部参照は省略されています。

クラスSは、次の場合に標準レイアウトクラスです
。—非標準レイアウトクラス(またはそのようなタイプの配列)または参照の非静的データメンバー
がない場合—仮想関数および仮想基本クラスがない場合
—すべての非静的データメンバーに対して同じアクセス制御があります
—非標準レイアウトの基本クラスは
ありません
— — 任意のタイプの最大1つの基本クラスサブオブジェクトがあります—すべての非静的データメンバーとビットフィールドがあります同じクラスで最初に宣言されたクラスとその基本クラス、および
— 基本クラスとしてのタイプ(以下で定義)のセットM(S)の要素がありません。108—
(X)は次のように定義されます。
— Xが非ユニオンクラスタイプであり、継承されていない可能性のある非静的データメンバーがない場合、セットM(X)は空です。
— Xが非ユニオンクラス型であり、その最初の非静的データメンバーの型がX0である場合(このメンバーは匿名ユニオンである可能性があります)、セットM(X)はX0とM(X0)の要素で構成されます。
— Xがユニオンタイプの場合、セットM(X)はすべてのM(Ui)とすべてのUiを含むセットのユニオンであり、各UiはXのi番目の非静的データメンバーのタイプです。—
Xの場合要素タイプXeの配列タイプで、セットM(X)はXeとM(Xe)の要素で構成されます。
— Xがクラスでも配列でもないタイプの場合、セットM(X)は空です。
[注:M(X)は、標準レイアウトクラスでXのゼロオフセットにあることが保証されているすべての非基本クラスサブオブジェクトのタイプのセットです。—end note]
[例:

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 example]
108)これにより、同じクラスタイプを持ち、最も派生した同じオブジェクトに属する2つのサブオブジェクトが同じアドレスに割り当てられないことが保証されます。

変更:

  1. 派生ツリー内の1つのクラスのみが非静的データメンバーを「持つ」という要件は、そのようなデータメンバーが最初に宣言されたクラスを参照し、継承される可能性のあるクラスではないことを明確にし、この要件を非静的ビットフィールドに拡張しました。 。また、標準レイアウトクラスには「指定されたタイプの基本クラスサブオブジェクトが最大で1つある」ことも明記しました。DR1813DR1881を参照してください
  2. standard-layoutの定義では、基本クラスの型が最初の非静的データメンバーと同じ型になることは決してありません。これは、オフセット0のデータメンバーの型が基本クラスと同じになるのを避けるためです。C ++ 17標準は、「標準レイアウトクラスでゼロオフセットになることが保証されているすべての非基本クラスサブオブジェクトのタイプのセット」のより厳密で再帰的な定義を提供して、そのようなタイプを禁止していますあらゆる基本クラスの型から。DR1672DR2120を参照してください。

注:新しい言語は公開されたC ++ 14標準には含まれていませんが、C ++標準委員会は、欠陥レポートに基づく上記の変更をC ++ 14に適用することを意図していました。これはC ++ 17標準に含まれています。


注意:回答を更新しました。標準のレイアウト変更の欠陥にはCD4ステータスがあり、実際にはC ++ 14に適用されます。これが、私の回答にそれらを含めなかった理由でした。これは、私の回答を書いた後に起こりました。
Shafik Yaghmour

注、私はこの質問について賞金を獲得しました。
Shafik Yaghmour

@ShafikYaghmourに感謝します。欠陥レポートのステータスを確認し、それに応じて回答を変更します。
ThomasMcLeod

@ ShafikYaghmour、C ++ 14プロセスのレビューの結果、2014年6月のRapperswil会議でこれらのDRが「受け入れられた」一方で、2014年2月のIssaquah会議の言語がC ++ 14になったようです。isocpp.org/blog/2014/07/trip-report-summer-iso-c-meetingを参照してください「ISO規則に従い、C ++ワーキングペーパーの編集を正式に承認していません。」何か不足していますか?
ThomasMcLeod 2018

それらは「CD4」ステータスであり、C ++ 14モードで適用する必要があることを意味します。
Shafik Yaghmour 2018

14

何が変わるのか

この質問の残りの明確なテーマに従って、集計の意味と使用法はすべての標準で変わり続けています。地平線にはいくつかの重要な変更があります。

ユーザー宣言のコンストラクターを持つ型P1008

C ++ 17では、この型はまだ集合体です:

struct X {
    X() = delete;
};

そして、それは、それX{}が集合体の初期化であり、コンストラクターの呼び出しではないため、依然としてコンパイルされます。参照:プライベートコンストラクターがプライベートコンストラクターではない場合

C ++ 20では、制限は次のものを要求するように変更されます。

ユーザー提供、explicit継承、継承のコンストラクタはありません

ユーザー宣言または継承コンストラクターなし

これは、C ++ 20ワーキングドラフトに採用されました。C ++ 20では、リンクされた質問のXhereもthe Cも集約ではありません。

これは、次の例のヨーヨー効果ももたらします。

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

++ 11月14日Cにおいて、Bあったしないように、基本クラスの集合体によりB{}実行値初期化れるコールB::B()のコールA::A()がアクセス可能である点で、。これは整形式でした。

C ++ 17では、B基本クラスが許可されたために集約になり、集約のB{}初期化が行われました。これには、Aからのコピーリスト初期化が必要ですが、アクセスできないの{}コンテキストの外側からBです。C ++ 17では、これは形式auto x = B();が正しくありません(ただし問題ありません)。

現在のC ++ 20では、上記のルールの変更により、B(基本クラスのためではなく、ユーザーが宣言したデフォルトコンストラクターのために(デフォルトであっても)集合体でなくなりました)。これでB、のコンストラクターに戻り、このスニペットは整形式になります。

括弧で囲まれた値のリストP960からの集計の初期化

発生する一般的な問題はemplace()、集計で-スタイルコンストラクターを使用することです:

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

は有効ではないemplace初期化を効果的に実行しようとするためX(1, 2)、これは機能しません。典型的な解決策は、コンストラクターをに追加することXですが、この提案(現在はCoreを介して機能しています)を使用すると、集約は、正しいことを行う合成コンストラクターを効果的に持ち、通常のコンストラクターのように動作します。上記のコードは、C ++ 20でそのままコンパイルされます。

集約P1021(具体的にはP1816)のクラステンプレート引数控除(CTAD

C ++ 17では、これはコンパイルされません。

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

Point p{1, 2}; // error

ユーザーは、すべての集計テンプレートに対して独自の控除ガイドを作成する必要があります。

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

しかし、これはある意味では「明白なこと」であり、基本的には単なる定型文であるため、言語がこれを行います。この例はC ++ 20でコンパイルされます(ユーザー提供の控除ガイドは必要ありません)。


私は賛成票を投じますが、これを追加するのは少し早いと思いますが、C ++ 2xが完了する前にこれを変更するような重大な問題が発生することは知りません。
Shafik Yaghmour 2018

@ShafikYaghmourええ、おそらく早すぎるでしょう。しかし、SDが新しい言語機能の期限であることを考えると、これらは私が認識している唯一の2つの処理中です-最悪の場合、これらのセクションの1つを後でブロック削除するだけですか?バウンティで活発な質問を見ただけで、忘れる前にチャイムを鳴らすのに良い時だと思いました。
バリー

私は理解しています、私は似たような場合に何度か誘惑されてきました。何かが大きく変わるといつも心配して、結局書き直さなければならない。
Shafik Yaghmour 2018

@ShafikYaghmourここでは何も変わらないようです:)
バリー

C ++ 20がすでにリリースされている今、これが更新されることを願っています
誰もAtAll
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.