集約の初期化のために空の基本クラスを非表示にする


9

次のコードを検討してください。

struct A
{
    // No data members
    //...
};

template<typename T, size_t N>
struct B : A
{
    T data[N];
}

これは、Bを初期化する方法ですB<int, 3> b = { {}, {1, 2, 3} }; 。基本クラスの不要な空の{}を避けたいのです。ここでJarod42によって提案された解決策がありますが、要素のデフォルトの初期化では機能しません。B<int, 3> b = {1, 2, 3};問題B<int, 3> b = {1};はありませんが、デフォルトb.data[1]b.data[2]はありません。デフォルトで0に初期化されていないため、コンパイラエラーが発生します。基本クラスを構築から「隠す」方法はありますか(またはc ++ 20で存在するでしょう)?


2
コンストラクタを追加しないのはなぜtemplate<class... Ts> B(Ts... args) : data{args...} {}ですか?
平均

なぜコメントなのですか?動作しているようです、笑
user7769147

これは明らかな解決策なので、使用しない理由があると思いました。:)
19

xDは簡単すぎました。回答として書いた場合は、それを受け入れます
user7769147

回答:


6

最も簡単な解決策は、可変長コンストラクターを追加することです。

struct A { };

template<typename T, std::size_t N>
struct B : A {
    template<class... Ts, typename = std::enable_if_t<
        (std::is_convertible_v<Ts, T> && ...)>>
    B(Ts&&... args) : data{std::forward<Ts>(args)...} {}

    T data[N];
};

void foo() {
    B<int, 3> b1 = {1, 2, 3};
    B<int, 3> b2 = {1};
}

{...}初期化リストに提供する要素の数がよりも少ない場合N、配列の残りの要素dataはによって値で初期化されT()ます。


3
これが集計の初期化と異なる理由を見つけました。B<Class, 5> b = {Class()}; Class最初に構築してから移動することを検討する場合、集約の初期化Classを使用することによって構築されますが、移動は含まれません
user7769147

@ user7769147、良い点。std::tuple引数を取り、それらを使用してインプレースでオブジェクトを構築できます。しかし、構文はかなり面倒になります。
19

1
私はこの問題を解決する解決策をランダムに見つけました。これを受け入れられた答えのままにして、可用性を感謝します:)
user7769147

4

C ++ 20以降、指定された初期化子集約初期で使用できました。

B<int, 3> b = { .data {1} }; // initialize b.data with {1}, 
                             // b.data[0] is 1, b.data[1] and b.data[2] would be 0

それは私にはまだ冗長すぎる、それは最小限の例でした。私のアレイメンバーは、ユーザーが無視するべき奇妙な名前を持っています
user7769147

4

それでもコンストラクターを使用すると、次のようなことができます。

template<typename T, size_t N>
struct B : A
{
public:
    constexpr B() : data{} {}

    template <typename ... Ts,
              std::enable_if_t<(sizeof...(Ts) != 0 && sizeof...(Ts) < N)
                               || !std::is_same_v<B, std::decay_t<T>>, int> = 0>
    constexpr B(T&& arg, Ts&&... args) : data{std::forward<T>(arg), std::forward<Ts>(args)...}
    {}

    T data[N];
};

デモ

SFINAEは主に、疑似コピーコンストラクターの作成を回避するために行われますB(B&)

サポートするには追加のプライベートタグが必要ですB<std::index_sequence<0, 1>, 42>;-)


なぜ必要なの((void)Is, T())...ですか?単純に省略した場合はどうなりますか?残りの要素はT()デフォルトで値で初期化されませんか?
19

1
@Evg:確かに、単純化されました。残りの要素を値ではなくデフォルトで初期化することだけを恐れていました...
Jarod42

2

(方法がわからない)完全に機能し、Evgの回答の下で議論していた問題を解決する別の解決策を見つけました

struct A {};

template<typename T, size_t N>
struct B_data
{
    T data[N];
};

template<typename T, size_t N>
struct B : B_data<T, N>, A
{
    // ...
};

興味深いソリューション。しかし今では、を使用するthis->datausing B_data::data;data内部にアクセスする必要がありますB
19
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.