2016-04-02 5 views
10

あなたがタイプstd::vector<std::string>の変数を持っていて、初期化子リストで初期化しましょう:initializer_listの要素を移動するにはどうすればよいですか?

using V = std::vector<std::string>; 
V v = { "Hello", "little", "world", "of", "move", "semantics" }; 

コンパイラは、リテラル文字列ごとに一時的std::stringを作成し、これらを介して初期化子リストを作成し、ctorのを呼び出しますVの場合はベクターを作成します。 ctorはすべての文字列が一時的な文字列であることを知らないので、各文字列をコピーしています。

標準で、ベクトルctorが一時的なときに要素を移動させることができないものは見つかりませんでした。

何か不足していますか、イニシャライザリストを使用して不要なコピーが作成されていますか?私はこの問題が著しく非効率的なコードにつながるクラスを書いています。不必要なコピーを避けるための技術は、非常に高く評価されます。標準は、実際の引数として中括弧初期化子から、初期化子リストの引数を取るコンストラクタの呼び出しを定義するため、次のように

+0

良い質問。私はイニシャライザのリストがもともとテンポラリのシーケンスを渡して何かをすることを意図したものではないと思う。しかし今では、イニシャライザーリスト変数を宣言できます。このような長命のリストの各項目の内臓を吸うベクトルのためにはないでしょう。しかし、待って、初期値リストにrvalue refを持つコンストラクタ。多分? –

+0

@ Cheersandhth.-Alf私はconst-ref、non-const-ref、rvalue-refで初期化子リストをオーバーロードすることができると思います。次に、要素をrvalue-ref初期化子リストから手動で移動することができます。洗練されていないし、私は現在、基になるストレージ(ベクトルとマップ)として使用しているSTLコンテナで動作しませんが、何よりも優れています。ありがとう。 –

+0

[initializer \ _listとmove semantics]の複製(https://stackoverflow.com/questions/8193102/initializer-list-and-move-semantics) –

答えて

4

は(強調追加)、initializer_list<string>からのコピーを回避するための方法はありません。

実装タイプ 0123のN要素の一時的な配列を割り当てられているかのようC++ 14§8.5.4/ 5

タイプstd::initializer_list<E>のオブジェクトが初期化リストから構成されていますNが 初期化子リスト内の要素

IMHOの数があるが、これは本当に残念なことです。

回避方法(自分のクラス用)はinitializer_list<char const*>を受け入れることです。


次に、std::vector<string>に適用される回避策の例を示します。そのため、クラスのコードを制御しない場所では、データ配列(実際にはinitializer_list)を明示的に宣言します。これは単に初期化子リスト機構を避けるために意図されたC++ 03、のようにされています

#include <vector> 
#include <initializer_list> 
#include <iostream> 
#include <iterator>    // std::begin, std::end 
using namespace std; 

struct My_string 
{ 
    char const* const ps; 

    My_string(char const* const s) 
     : ps(s) 
    { 
     cout << " My_string(*) <- '" << s << "'" << endl; 
    } 

    My_string(My_string const& other) 
     : ps(other.ps) 
    { 
     cout << " My_string(const&) <- '" << other.ps << "'" << endl; 
    }; 

    My_string(My_string&& other) 
     : ps(other.ps) 
    { 
     cout << " My_string(&&) <- '" << other.ps << "'" << endl; 
    }; 
}; 

auto main() -> int 
{ 
    cout << "Making vector a." << endl; 
    vector<My_string> const a = {"a1", "a2", "a3"}; 
    cout << "Making data for vector b." << endl; 
    auto const b_data   = { "b1", "b2", "b3" }; 
    cout << "Making vector b." << endl; 
    vector<My_string> const b(begin(b_data), end(b_data)); 
} 

出力:いくつかの思考の後

 
Making vector a. 
    My_string(*) <- 'a1' 
    My_string(*) <- 'a2' 
    My_string(*) <- 'a3' 
    My_string(const&) <- 'a1' 
    My_string(const&) <- 'a2' 
    My_string(const&) <- 'a3' 
Making data for vector b. 
Making vector b. 
    My_string(*) <- 'b1' 
    My_string(*) <- 'b2' 
    My_string(*) <- 'b3' 
+0

バリデーションテンプレートコンストラクタを使用することもできます。 – o11c

+0

'const E'?ありがとうございます*本当に残念です。これには何らかの理由がありますか?さもなければ、rvalueイニシャライザリストを検出することは、少なくとも私が手動で動かせるようにするでしょうが、 'const E'で、それは違法と思われます! (そして私は実際に 'std :: string'を使用していません。コピーするのに高価になるかもしれないタイプを使用しています) –

+0

私は' mutable'を使って解決策を見つけたと思います。あなたはそれについてどう思いますか? –

2

、私はmutableに基づいて解決策を考え出しました。もう1つの答えはまだ大体正しいですが、変更可能なメンバーを持つプロキシを作成して最上位のconst -nessを取り除き、そこから要素を移動することができます。したがって、初期化子リストを取るメソッドは、const-ref初期化子リストとrvalue-refバージョンのためにオーバーロードして、いつそれらが動くことができるかを知るべきです。

実際の使用例ですが、最初は恣意的に見えるかもしれませんが、実際の使用例では問題は解決しました。

#include <iostream> 
#include <vector> 

// to show which operations are called 
struct my_string 
{ 
    const char* s_; 
    my_string(const char* s) : s_(s) { std::cout << "my_string(const char*) " << s_ << std::endl; } 
    my_string(const my_string& m) : s_(m.s_) { std::cout << "my_string(const my_string&) " << s_ << std::endl; } 
    my_string(my_string&& m) noexcept : s_(m.s_) { std::cout << "my_string(my_string&&) " << s_ << std::endl; } 
    ~my_string() { std::cout << "~my_string() " << s_ << std::endl; } 
}; 

// the proxy 
struct my_string_proxy 
{ 
    mutable my_string s_; 

    // add all ctors needed to initialize my_string 
    my_string_proxy(const char* s) : s_(s) {} 
}; 

// functions/methods should be overloaded 
// for the initializer list versions 

void insert(std::vector<my_string>& v, const std::initializer_list<my_string_proxy>& il) 
{ 
    for(auto& e : il) { 
     v.push_back(e.s_); 
    } 
} 

void insert(std::vector<my_string>& v, std::initializer_list<my_string_proxy>&& il) 
{ 
    for(auto& e : il) { 
     v.push_back(std::move(e.s_)); 
    } 
} 

int main() 
{ 
    std::vector<my_string> words; 
    insert(words, { {"Hello"}, {"initializer"}, {"with"}, {"move"}, {"support"} }); 
} 

Live example

関連する問題