私はC++ 0xを使いこなしており、g ++ 4.6でテストしています
次のコードを試してみましたが、うまくいくと思いましたが、コンパイルしません。エラーが表示されます:
incompatible types in assignment of ‘std::initializer_list<const int>’ to ‘const int [2]’
struct Foo
{
int const data[2];
Foo(std::initializer_list<int const>& ini)
: data(ini)
{}
};
Foo f = {1,3};
初期化子リストコンストラクターの代わりに、可変個引数テンプレートコンストラクターを使用できます。
struct foo {
int x[2];
template <typename... T>
foo(T... ts) : x{ts...} { // note the use of brace-init-list
}
};
int main() {
foo f1(1,2); // OK
foo f2{1,2}; // Also OK
foo f3(42); // OK; x[1] zero-initialized
foo f4(1,2,3); // Error: too many initializers
foo f5(3.14); // Error: narrowing conversion not allowed
foo f6("foo"); // Error: no conversion from const char* to int
}
編集:もしあなたがconstnessなしで生きられるなら別の方法は初期化をスキップして関数本体の配列を埋めることです:
struct foo {
int x[2]; // or std::array<int, 2> x;
foo(std::initializer_list<int> il) {
std::copy(il.begin(), il.end(), x);
// or std::copy(il.begin(), il.end(), x.begin());
// or x.fill(il.begin());
}
}
ただし、この方法では、前のソリューションが提供するコンパイル時の境界チェックが失われます。
私が知る限り、コンストラクターの関数引数のリスト初期化(8.5.4/1)を使用することは有効であり、上記の問題の多くを解決します。ただし、ideone.comのGCC 4.5.1は、コンストラクターとの一致に失敗し、拒否します。
#include <array>
struct Foo
{
std::array< int, 2 > const data;
Foo(std::array<int, 2> const& ini) // parameter type specifies size = 2
: data( ini )
{}
};
Foo f( {1,3} ); // list-initialize function argument per 8.5.4/1
initializer_list
、使用できますreinterpret_cast
は、initializer_list
をCスタイルの配列に。
Foo(std::initializer_list<int> ini) // pass without reference- or cv-qualification
: data( reinterpret_cast< std::array< int, 2 > const & >( * ini.begin() )
すばらしい JohannesDの答え へのほんの小さな追加です。
foo
コンストラクターに引数が渡されない場合、配列はデフォルトで初期化されます。ただし、基礎となる配列を初期化せずに残したい場合があります(パフォーマンス上の理由から)。 variadic-templatedとともにデフォルトコンストラクターを追加することはできません。回避策は、引数なしのコンストラクタと区別するために、variadic-templatedコンストラクタへの追加の引数です。
template<class T, size_t rows, size_t cols>
class array2d
{
std::array<T, rows * cols> m_Data;
public:
array2d() {}
template <typename T, typename... Types>
array2d(T t, Types... ts) : m_Data{ { t, ts... } } {}
};
したがって、オブジェクトをブレース初期化するか、初期化せずに残すことができます。
array2d<int, 6, 8> arr = { 0, 1, 2, 3 }; // contains 0, 1, 2, 3, 0, 0, 0, ...
array2d<int, 6, 8> arr2; // contains garbage
2016年7月31日更新
すぐに3年が経ち、コンパイラーの実装者は、可変コンストラクターの存在下でデフォルトコンストラクターがあいまいにならないレベルまで、製品の標準準拠を改善しました。したがって、実際には、コンストラクターを明確にするために、可変引数コンストラクターにT t
引数を追加する必要はありません。
どちらも
array2d() {}
そして
array2d() = default;
オブジェクトが引数なしで構築されている場合、配列は初期化されません。この動作は、すべての主要なコンパイラで一貫しています。完全な例( rextester ):
#include <array>
#include <iostream>
template<class T, size_t rows, size_t cols>
class array2d
{
public:
std::array<T, rows * cols> m_Data;
array2d() = default;
template <typename... Types>
array2d(Types... ts) : m_Data{ { ts... } } {}
};
int main()
{
array2d<int, 6, 8> arr_init = { 0, 1, 2, 3 };
array2d<int, 6, 8> arr_default;
std::cout << "Initialized: \n";
for(const auto& a : arr_init.m_Data)
std::cout << a << " ";
std::cout << "\n";
std::cout << "Default: \n";
for(const auto& a : arr_default.m_Data)
std::cout << a << " ";
std::cout << "\n";
}
出力:
Initialized:
0 1 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Default:
2 0 -519559849 32558 1 32558 0 0 -519634912 32558 -526739248 32558 1 0 2 0 6295032 0 -519531243 32558 0 0 -1716075168 32765 6295648 0 4196192 0 6295648 0 -526527271 32558 1 0 2 0 6295032 0 4196845 0 124 0 0 0 4196768 0 4196518 0
デフォルトのコンストラクターを削除しても、変数のコンストラクターが呼び出され、配列がデフォルトで初期化されます(この例ではすべてゼロになります)。
このスレッドにぶつかり、これらの事実に注意を向けてくれた@Alekに感謝します。また、コンパイラ開発に熱心に取り組んでいるすべての人々に感謝します。
議論によれば こちら :
potatoswatterの2番目のソリューションの正しい構文は次のとおりです。
Foo f( {{1,3}} ); //two braces
少しい、一般的な使用法と一致しない
配列は他の型とは異なりません(また、std :: initializer_listをとるコンストラクターはありません)。
代わりにこれを試してください:
struct Foo
{
const std::vector<int> data;
Foo(std::initializer_list<int> ini) : data(ini)
{}
};
これは機能しませんが:
#include <initializer_list>
struct Foo
{
const int data[2];
constexpr Foo(const std::initializer_list<int>& ini): data{ini} {}
};
Foo f = {1,3};
私はうまく動作するこの簡単なアプローチを見つけました:
struct Foo
{
const int data[2];
constexpr Foo(const int a, const int b): data{a,b} {}
};
Foo f = {1,3};
もちろん、要素がたくさんある場合は、可変引数テンプレートのアプローチの方がおそらく優れていますが、この単純なケースではおそらくこれで十分です。
つまり、初期化子リストからコンストラクタを明示的に定義する場合です。ほとんどのPODの場合、これはうまくいきます。
struct Foo
{
const int data[2];
};
Foo f = {1,3};
境界チェックを気にしない場合は、次のようにします。
struct Foo {
int const data[2];
Foo(std::initializer_list<int> ini)
: data{*std::begin(ini), *std::next(std::begin(ini), 1)} {}
};