web-dev-qa-db-ja.com

C ++:配列のコンストラクター初期化子

私は脳が痙攣しています... C++でオブジェクトの配列を適切に初期化するにはどうすればよいですか?

非配列の例:

_struct Foo { Foo(int x) { /* ... */  } };

struct Bar { 
     Foo foo;

     Bar() : foo(4) {}
};
_

配列の例:

_struct Foo { Foo(int x) { /* ... */  } };

struct Baz { 
     Foo foo[3];

     // ??? I know the following syntax is wrong, but what's correct?
     Baz() : foo[0](4), foo[1](5), foo[2](6) {}
};
_

編集:ワイルドでクレイジーな回避策のアイデアは歓迎されますが、私の場合は助けにはなりません。私はstd :: vectorや他のSTL構造が利用できない組み込みプロセッサに取り組んでおり、明白な回避策はデフォルトのコンストラクタを作成し、構築後に呼び出すことができる明示的なinit()メソッドを持つことです時間なので、イニシャライザをまったく使用する必要はありません。 (これは、Javaのfinalキーワード+コンストラクターの柔軟性に甘やかされているケースの1つです。)

62
Jason S

道はない。配列メンバーにはデフォルトのコンストラクターが必要であり、それが呼び出されると、コンストラクターで必要な初期化を行うことができます。

49
AProgrammer

C++ 11のこの質問を更新するだけで、これは実行可能になり、非常に自然になりました。

struct Foo { Foo(int x) { /* ... */  } };

struct Baz { 
     Foo foo[3];

     Baz() : foo{{4}, {5}, {6}} { }
};

これらのブレースは、さらに簡潔にするために省略できます。

struct Baz { 
     Foo foo[3];

     Baz() : foo{4, 5, 6} { }
};

多次元配列にも簡単に拡張できます:

struct Baz {
    Foo foo[3][2];

    Baz() : foo{1, 2, 3, 4, 5, 6} { }
};
33
Barry

現在、配列メンバーに初期化リストを使用することはできません。あなたはそれを困難な方法で行うのにこだわっています。

class Baz {
    Foo foo[3];

    Baz() {
        foo[0] = Foo(4);
        foo[1] = Foo(5);
        foo[2] = Foo(6);
    }
};

C++ 0xでは、次のように記述できます。

class Baz {
    Foo foo[3];

    Baz() : foo({4, 5, 6}) {}
};
16

残念ながら、C++ 0xまで配列メンバーを初期化する方法はありません。

コンストラクター本体でstd :: vectorとPush_back the Fooインスタンスを使用できます。

Fooにデフォルトのコンストラクターを与えることができます(プライベートで、Bazを友達にすることができます)。

is copyable(boostまたはstd :: tr1)の配列オブジェクトを使用して、静的配列から初期化できます。

#include <boost/array.hpp>

struct Baz {

    boost::array<Foo, 3> foo;
    static boost::array<Foo, 3> initFoo;
    Baz() : foo(initFoo)
    {

    }
};

boost::array<Foo, 3> Baz::initFoo = { 4, 5, 6 };
7
UncleBens

C++ 0xautoキーワードとtemplate specializationを組み合わせて使用​​できます。たとえば、boost::make_array()という名前の関数(make_pair())。 Nが1つまたは2つの引数の場合、variant A as

_namespace boost
{
/*! Construct Array from @p a. */
template <typename T>
boost::array<T,1> make_array(const T & a)
{
    return boost::array<T,2> ({{ a }});
}
/*! Construct Array from @p a, @p b. */
template <typename T>
boost::array<T,2> make_array(const T & a, const T & b)
{
    return boost::array<T,2> ({{ a, b }});
}
}
_

およびバリアントB as

_namespace boost {
/*! Construct Array from @p a. */
template <typename T>
boost::array<T,1> make_array(const T & a)
{
    boost::array<T,1> x;
    x[0] = a;
    return x;
}
/*! Construct Array from @p a, @p b. */
template <typename T>
boost::array<T,2> make_array(const T & a, const T & b)
{
    boost::array<T,2> x;
    x[0] = a;
    x[1] = b;
    return x;
}
}
_

GCC-4.6と_-std=gnu++0x_および_-O3_は、まったく同じバイナリコードを生成します

_auto x = boost::make_array(1,2);
_

[〜#〜] a [〜#〜][〜#〜] b [〜#〜]の両方を使用する

_boost::array<int, 2> x = {{1,2}};
_

ただし、ユーザー定義型(UDT)の場合、バリアントBは余分なコピーコンストラクターになります。これは通常速度を低下させるため、回避する必要があります。

次の場合のように、明示的なchar配列リテラルで呼び出すと、_boost::make_array_エラーが発生することに注意してください。

_auto x = boost::make_array("a","b");
_

_const char*_リテラルは欺cept的を使用できるため、これは良いことだと思います。

Variadic templates、4.5以降のGCCで利用可能です。さらに使用して、各N単一テンプレート定義 of boost::make_array()に定義

_/*! Construct Array from @p a, @p b. */
template <typename T, typename ... R>
boost::array<T,1+sizeof...(R)> make_array(T a, const R & ... b)
{
    return boost::array<T,1+sizeof...(R)>({{ a, b... }});
}
_

これは予想どおりに機能します。最初の引数は_boost::array_テンプレート引数Tを決定し、他のすべての引数はTに変換されます。場合によってはこれは望ましくないかもしれませんが、可変テンプレートを使用してこれを指定できるかどうかはわかりません。

おそらくboost::make_array()はBoost Librariesに入るべきでしょうか?

3
Nordlöw

これはうまくいくようですが、私はそれが正しいと確信していません:

_#include <iostream>

struct Foo { int x; Foo(int x): x(x) { } };

struct Baz { 
     Foo foo[3];

    static int bar[3];
     // Hmm...
     Baz() : foo(bar) {}
};

int Baz::bar[3] = {4, 5, 6};

int main() {
    Baz z;
    std::cout << z.foo[1].x << "\n";
}
_

出力:

_$ make arrayinit -B CXXFLAGS=-pedantic && ./arrayinit
g++ -pedantic    arrayinit.cpp   -o arrayinit
5
_

買い手責任負担。

編集:いいえ、コモーはそれを拒否します。

別の編集:これは一種の不正行為であり、メンバーごとの配列初期化を別の場所にプッシュするだけです。したがって、Fooにはデフォルトのコンストラクターが必要ですが、_std::vector_がない場合は、必要な最低限の最低限を実装できます。

_#include <iostream>

struct Foo { 
    int x; 
    Foo(int x): x(x) { }; 
    Foo(){}
};

// very stripped-down replacement for vector
struct Three { 
    Foo data[3]; 
    Three(int d0, int d1, int d2) {
        data[0] = d0;
        data[1] = d1;
        data[2] = d2;
    }
    Foo &operator[](int idx) { return data[idx]; }
    const Foo &operator[](int idx) const { return data[idx]; }
};

struct Baz { 
    Three foo;

    static Three bar;
    // construct foo using the copy ctor of Three with bar as parameter.
    Baz() : foo(bar) {}
    // or get rid of "bar" entirely and do this
    Baz(bool) : foo(4,5,6) {}
};

Three Baz::bar(4,5,6);

int main() {
    Baz z;
    std::cout << z.foo[1].x << "\n";
}
_

_z.foo_は実際には配列ではありませんが、ベクトルと同じように見えます。 begin()およびend()関数をThreeに追加するのは簡単です。

2
Steve Jessop

配列にオブジェクトを作成するときは、デフォルトのコンストラクターのみを呼び出すことができます。

1
Robert

配列がクラスのデータメンバーである特定のケースでは、言語の現在のバージョンでca n'tを初期化します。そのための構文はありません。配列要素のデフォルトコンストラクターを提供するか、std::vectorを使用します。

スタンドアロン配列は、集約イニシャライザーで初期化できます

Foo foo[3] = { 4, 5, 6 };

残念ながら、コンストラクタ初期化リストに対応する構文はありません。

1
AnT

あなたはそれを行うことができますが、それはきれいではありません:

#include <iostream>

class A {
    int mvalue;
public:
    A(int value) : mvalue(value) {}
    int value() { return mvalue; }
};

class B {
    // TODO: hack that respects alignment of A.. maybe C++14's alignof?
    char _hack[sizeof(A[3])];
    A* marr;
public:
    B() : marr(reinterpret_cast<A*>(_hack)) {
        new (&marr[0]) A(5);
        new (&marr[1]) A(6);
        new (&marr[2]) A(7);
    }

    A* arr() { return marr; }
};

int main(int argc, char** argv) {
    B b;
    A* arr = b.arr();
    std::cout << arr[0].value() << " " << arr[1].value() << " " << arr[2].value() << "\n";
    return 0;
}

これをコードに追加する場合は、非常に正当な理由があると思います。

0
Moshev

少なくとも直接ではなく、このコンテキストで使用できる配列構築構文はありません。あなたが達成しようとしていることは、次のようなものによって達成できます。

Bar::Bar()
{
    static const int inits [] = {4,5,6};
    static const size_t numInits = sizeof(inits)/sizeof(inits[0]);
    std::copy(&inits[0],&inits[numInits],foo);  // be careful that there are enough slots in foo
}

...しかし、Fooにデフォルトのコンストラクタを与える必要があります。

0
John Dibling

これはあなたの参考のための私のソリューションです:

struct Foo
{
    Foo(){}//used to make compiler happy!
    Foo(int x){/*...*/}
};

struct Bar
{
    Foo foo[3];

    Bar()
    {
        //initialize foo array here:
        for(int i=0;i<3;++i)
        {
            foo[i]=Foo(4+i);
        }
    }
};
0
user7967189

ねじれた心からのアイデア:

class mytwistedclass{
static std::vector<int> initVector;
mytwistedclass()
{
    //initialise with initVector[0] and then delete it :-)
}

};

オブジェクトをインスタンス化する前に、このinitVectorをuに設定します。次に、オブジェクトがパラメーターで初期化されます。

0
the100rabh