C++と色々

主にC++やプログラムに関する記事を投稿します。

Aggregates 集成体

以下staticデータメンバと明示しない限り、非staticデータメンバを単にデータメンバと表現します。

Aggregates とは

Aggregates(以下、集成体)は配列と、以下の条件を満たしたクラスです。

  • ユーザ定義のコンストラクタ(コピー、ムーブ含む)が無いこと。つまりコンストラクタは全く記述されていないか、default指定されている状態。
  • 全てのデータメンバがpublicであること。staticデータメンバの可視性は影響しない。
  • 基本クラスが無いこと
  • 仮想関数が無いこと
struct Base {};

// 集成体ではない
class B : Base { // 基本クラスがある
    // publicではないデータメンバ
    int a;

public:
    // 仮想関数
    virtual void f() {}
    // ユーザ定義コンストラクタ
    B(B const&) {}
};

// 集成体
struct A {
    double d;
    B b;
    void g() {}
};

// 集成体
B bs[3];

注意点

  • 集成体のデータメンバが非集成体でも良い。可視性以外にデータメンバ自体に要求されるコンセプトはない
  • データメンバがインライン初期化を持っていても良い
  • 以上より、あるクラスが集成体を満たす時TriviallyCopyableやTriviallyDefaultConstructibleとは限らないし、StandardLayoutとも限らない
#include <type_traits>

struct X {
    virtual void f() {}
};

struct aggregate {
    X x;
    int i;
};

int main()
{
    aggregate agg = {{}, 42};
    
    static_assert(!std::is_trivially_copy_constructible<aggregate>{}, "");
    static_assert(!std::is_trivially_default_constructible<aggregate>{}, "");
    static_assert(!std::is_standard_layout<aggregate>{}, "");
}

特徴

集成体はC言語の構造体と同じような初期化が行えるという特徴があります。

集成体だとできること

集成体を初期化リストで初期化した場合、初期化リストの要素の順序と集成体のデータメンバの順序が対応します。それぞれの要素がコピーされ初期化されます。

A a = {0.0, B{}};

配列数より初期化リストが足りない場合、初期化リストで明示的に初期化されなかったデータメンバ(仮に型Tとする)は={}またはT{}されたのと同じ効果の初期化が行われます。つまりゼロ初期化になります。

A as[3] = {A{}, {}}; //OK
A as2[3] = {}; // OK

要素が非集成体の場合は省略することはできず、明示的に初期化しなければなりません。

B bs[3] = {B{}, B(), {}};
// もしB(int)があった場合
B bs3[] = {1, B{2}, B(3), {4}};

B(int)がexplicit指定されていた場合、B(1)B{1}はOKですが、1{1}はNGです。

参考

N4431 §8.5.1