11

編集:始める前に、この質問は;の適切な使用法に関するものではありません。std::initializer_list便利な構文が必要な場合に何を渡す必要があるかについてです。話題にとどめていただきありがとうございます。


C++11 ではstd::initializer_list、波括弧初期化リスト引数を受け入れる関数を定義することが導入されています。

struct bar {
    bar( char const * );
    bar( int );
} dog( 42 );

fn foo( std::initializer_list< bar > args );

foo( { "blah", 3, dog } );

構文は素晴らしいですが、内部ではさまざまな問題のために不快です。

  • 意味のある移動はできません。上記の関数dogはリストからコピーする必要があります。これは、移動構築または省略に変換できません。移動専用タイプは一切使用できません。(まあ、const_cast実際には有効な回避策です。そうすることに関する記事があれば、それを見てみたいです。)

  • constexprセマンティクスもありません。(これは C++1y で間もなくリリースされます。ただし、これは小さな問題です。)

  • const他の場所のように伝播しません。はinitializer_list決してありませんconstが、その内容は常にあります。(コンテンツを所有していないため、コピーに書き込みアクセスを与えることはできませんが、どこにでもコピーすることはほとんど安全ではありません。)

  • オブジェクトはそのinitializer_listストレージを所有していません (yikes)。ストレージを提供する完全に分離された裸の配列 (yikes) との関係は、バインドされた一時的な参照の関係 (4 倍の yikes) としてあいまいに定義されます (yikes)。

これらの問題はやがて修正されると信じていますが、今のところ、ハードコーディングせずに利点を得るためのベストプラクティスはありinitializer_listますか? それに直接依存することを回避するための文献や分析はありますか?

明らかな解決策は、 などの標準コンテナを値渡しすることですstd::vector。オブジェクトが からコピーされると、initializer_list値によって渡されるようにムーブ構築され、コンテンツを移動できます。改善は、スタック上にストレージを提供することです。initializer_list優れたライブラリは、 、array、およびのほとんどの利点をvector、前者を使用しなくても提供できる場合があります。

リソースはありますか?

4

1 に答える 1

6

便利な構文が必要な場合に何を渡す必要があるかについてです。

サイズの利便性が必要な場合 (つまり、ユーザー{}は関数呼び出しや単語を使用せずにリストを入力するだけです)、適切な のすべての権限と制限を受け入れる必要initializer_listがあります。何らかの形式の などarray_ref、別のものに変換しようとしても、それらの間に仲介者initializer_listが必要です。つまり、出くわすことができないなど、遭遇した問題を回避することはできません。

を通過する場合はinitializer_list、これらの制限を受け入れる必要があります。initializer_listつまり、特定のセマンティクスを持つ何らかの形式のコンテナを受け入れる必要があります。また、代替オブジェクトの構築で同じ問題が発生しないように、代替タイプは集約である必要があります。

std::arrayしたがって、おそらくユーザーに(または言語配列)を作成させ、それを渡すことを検討しているでしょう。array_ref関数は、任意のサイズの任意の配列から構築できる何らかの形式のクラスを取ることができるため、消費する関数は 1 つのサイズに制限されません。

ただし、サイズの利便性は失われます。

foo( { "blah", 3, dog } );

対。

foo( std::array<bar, 3>{ "blah", 3, dog } );

ここで冗長性を避ける唯一の方法は、パラメーターとしてfootakeを使用することです。std::arrayこれは、特定の固定サイズの配列しかとれないことを意味します。また、仲介dynarrayを使用するため、C++14 の提案された を使用できませんでした。initializer_list

最終的に、値のリストを渡すために統一された初期化構文を使用するべきではありません。物のリストを渡すためではなく、オブジェクトを初期化するためのものです。std::initializer_list同じ型の値の任意の長いリストから特定のオブジェクトを初期化するために使用することを唯一の目的とするクラスです。これは、言語構造 (ブレース初期化リスト) とこれらの値が供給されるコンストラクターとの間の中間オブジェクトとして機能するために存在します。initializer_listこれにより、一致する値の波括弧初期化リストが与えられたときに、特定のコンストラクター (コンストラクター) を呼び出すことをコンパイラーに認識させることができます。

これが、クラスが存在する理由のすべてです。

したがって、このクラスは、考案された目的にのみ使用する必要があります。このクラスは、ブレース初期化リストから値のリストを取得するようにコンストラクターにタグを付けるために存在します。したがって、そのような値を取るコンストラクターに対してのみ使用する必要があります。

foo内部型 (直接公開したくない) とユーザー提供の値のリストとの間の仲介役として機能する関数がある場合は、 へのパラメーターとして何か他のものを取る必要がありますfoo。必要なセマンティクスを持つもので、内部型にフィードできます。

initializer_listまた、あなたはsと動きについて誤解しているようです。から移動することはできませんが、initializer_list確実に移動できます

foo( { "blah", 3, std::move(dog) } );

内部dog配列の 3 番目のエントリは移動構築されます。

于 2013-07-18T06:27:53.757 に答える