私は脳が痙攣しています... 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つです。)
道はない。配列メンバーにはデフォルトのコンストラクターが必要であり、それが呼び出されると、コンストラクターで必要な初期化を行うことができます。
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} { }
};
現在、配列メンバーに初期化リストを使用することはできません。あなたはそれを困難な方法で行うのにこだわっています。
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}) {}
};
残念ながら、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 };
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に入るべきでしょうか?
これはうまくいくようですが、私はそれが正しいと確信していません:
_#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に追加するのは簡単です。
配列にオブジェクトを作成するときは、デフォルトのコンストラクターのみを呼び出すことができます。
配列がクラスのデータメンバーである特定のケースでは、言語の現在のバージョンでca n'tを初期化します。そのための構文はありません。配列要素のデフォルトコンストラクターを提供するか、std::vector
を使用します。
スタンドアロン配列は、集約イニシャライザーで初期化できます
Foo foo[3] = { 4, 5, 6 };
残念ながら、コンストラクタ初期化リストに対応する構文はありません。
あなたはそれを行うことができますが、それはきれいではありません:
#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;
}
これをコードに追加する場合は、非常に正当な理由があると思います。
少なくとも直接ではなく、このコンテキストで使用できる配列構築構文はありません。あなたが達成しようとしていることは、次のようなものによって達成できます。
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にデフォルトのコンストラクタを与える必要があります。
これはあなたの参考のための私のソリューションです:
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);
}
}
};
ねじれた心からのアイデア:
class mytwistedclass{
static std::vector<int> initVector;
mytwistedclass()
{
//initialise with initVector[0] and then delete it :-)
}
};
オブジェクトをインスタンス化する前に、このinitVector
をuに設定します。次に、オブジェクトがパラメーターで初期化されます。