2016-07-14 8 views
3

私は、コンストラクタ関連部材内にその引数を格納し、単純な労働組合があります。バリアント関数のテンプレート引数をユニオンベクトルに格納する最も効率的な方法は?

:私は、これらの型の引数をとり、テンプレートの再帰を使用して組合のベクトルに格納する可変引数のコンストラクタを持って

union Data 
{ 
    Data(int i) : _i(i) { } 
    Data(double d) : _d(d) { } 
    Data(char c) : _c(c) { } 

    int _i; 
    double _d; 
    char _c; 
}; 

template<typename... Ts> 
DataStore(Ts... ts) 
{ 
    _data.reserve(sizeof...(ts)); 
    store(ts...); 
} 

template<typename T, typename... Ts> 
void store(T t, Ts... ts) 
{ 
    _data.push_back(t); 
    store(ts...); 
} 

void store() 
{ 
    // terminal condition 
} 

これにより、引数の数に一致する一連のvector::push_backコールが発生します。

これは、ユニオンベクターに最も効率的に/最も速い方法ですか?

私はこれをより速くするために採用することができる任意のトリック(x86-64/Linuxに固有であり得る)がありますか?

の作業例:

#include <iostream> 
#include <vector> 

union Data 
{ 
    Data(int i) : _i(i) { } 
    Data(double d) : _d(d) { } 
    Data(char c) : _c(c) { } 

    int _i; 
    double _d; 
    char _c; 
}; 

struct DataStore 
{ 
    template<typename... Ts> 
    DataStore(Ts... ts) 
    { 
     _data.reserve(sizeof...(ts)); 
     store(ts...); 
    } 

    template<typename T, typename... Ts> 
    void store(T t, Ts... ts) 
    { 
     _data.push_back(t); 
     store(ts...); 
    } 

    void store() 
    { 
     // terminal condition 
    } 

    std::vector<Data> _data; 
}; 

int main() 
{ 
    DataStore d(1, 2.3, 'c'); 

    std::cout << d._data.size() << '\n' 
       << d._data[0]._i << '\n' 
       << d._data[1]._d << '\n' 
       << d._data[2]._c << '\n'; 

    return 0; 
} 
+0

emplace_backが役立ちますか?あなたがそこに持っている現在の組合ではないかもしれません。より複雑な型が必要になるでしょう。*私はあなたが技術的に標準化すべきだと考えています。 – Borgleader

+0

@Borgleader私の特定のユースケースでは基本型しか格納しないので、 'std :: forward'は助けにならないでしょうAFAIK –

答えて

4

ご意見は、あなたが初期化子リストを使用して簡単なアプローチに対してだを示しているので、あなたがこれを行うことができます:

template<typename... T> 
DataStore(T&&... ts) 
{ 
    _data.reserve(sizeof...(ts)); 
    char dummy[] = { (_data.emplace_back(ts), '0')... }; 
} 

をまだ配列を作成しますが、それはできcharの配列だことおそらく最適化されており、再帰はなく、関数テンプレートstoreのインスタンス化はあまり必要ありません。

配列の各要素のイニシャライザは、パラメータパック内のオブジェクトの1つをベクトルに(左から右の順序で)挿入します。 emplace_backを使用すると、DataStoreコンストラクタに任意の型を渡すことができるため、オブジェクトが挿入されるまでDataオブジェクトを作成することはありません。それがために作ら何のコピーが存在しないことを意味する:あなたはinitializer_listを使用することを決定しない場合

DataStore d{ 1, '2', 3.0 }; 

(。スタック上だけで使用されていないchar[3]配列)

、あなたがいないデータストア内で、すぐにそれを作成する必要がありますコンストラクタ:

DataStore(std::initializer_list<Data> list) : _data(list) { } 

一時的な配列がそのコンストラクタの外部で作成されるように今、あなたは、DataStoreコンストラクタにData任意の数のオブジェクトを渡すことができ、その後、に直接渡されますメンバー。これを使用して、DataStore d{ Data{1}, Data{'2'}, Data{3.0} }は3つのオブジェクトの配列を作成し、それぞれをベクトルにコピーします。

このコンストラクタを使用すると、DataStore{1, '2', 3.0}を実行できません。コンパイラは異なるタイプのbraced-init-listからinitializer_list<Data>を作成しないためです。

+0

それに対して死んでいるわけではなく、私は教育的な決定をすることができるようにもっと理解したいだけです。この場合、一時的なコピーにペナルティがない場合は、それは素晴らしいでしょう。 –

+2

あなたのタイプのコピーは、ちょうど高価ではなく、8バイトの 'memcpy'になります。それはあなたが渡している引数の数に依存します。私はゼロコピーソリューションと 'initializer_list'の両方についてより詳しく議論する答えを編集しました。 –

1

どうちょうど約:あなたが直接あなたにも労働組合の内部で保存の基本的な型を渡すことができるように

template<typename... Ts> 
DataStore(Ts... ts) 
{ 
    _data = {ts...}; 
} 

これは、emplace_backを利用します。

+0

これは' std :: initializer_list'を正しく作成するでしょうか? [このブログの投稿](https://akrzemi1.wordpress.com/2016/07/07/the-cost-of-stdinitializer_list/)によると、initialiser_listがコピーを必要とするため、隠されたコストがあります。記事から引用すると、必要なすべての要素がコピー構築された一時配列が作成されます。* –

+1

'initializer_list'を使って、より効率的なアセンブリコード(g ++ 5.3)が生成されています。おそらく、ここでは些細な型を使用しており、コンパイラは不要なコピーをスマートに最適化しているからです。 – Arunmu

5

_dataを直接初期化できます。

template<typename... Ts> 
DataStore(Ts... ts) : _data{ts...} 
{} 
+1

これは 'std :: initializer_list'を正しく作成するでしょうか? [このブログの投稿](https://akrzemi1.wordpress.com/2016/07/07/the-cost-of-stdinitializer_list/)によると、initialiser_listがコピーを必要とするため、隠されたコストがあります。記事から引用すると、必要なすべての要素がコピー構築された一時配列が作成されます* –

+0

中括弧は 'std :: initializer_list'ではありません。彼らは決してありません。あなたはルールと混同されています。ここでは、 'auto'宣言された変数を中括弧で初期化して定義すると、コンパイラはその型を' std :: initializer_list'として推論し、それをリストで初期化します。 – KABoissonneault

+0

ああ、待ってください。私はあなたが何を意味するかを見ます。 'std :: vector'でbrace-initializerを使うと' std :: initializer_list'コンストラクタが呼び出されます。 – KABoissonneault

関連する問題