2017-10-13 9 views
3

std::atomic<int>のように、移動またはコピーのコンストラクタを持たないオブジェクトの固定サイズのstd::vectorを構築したいとします。この場合、基底のstd::atomicクラスには、1つの引数のコンストラクタがあり、デフォルトのコンストラクタ(値を0に初期化する)と同様に、intをとります。引数は最初initializer_listの作成の一部として、ベクトルの要素型Tに変換し、そのコピーまたはコンストラクタを移動しているため、std::vector<std::atomic<int>> v{1,2,3}ようinitializer_list構文は動作しません使用std :: vectorの配置のような構造

が呼び出されます。

Iのベクターを構築デフォルト、次いで後の要素を変異させることができる std::atomic<int>の特定の場合において

:しかし、醜い非効率的であることに加えて、それは多くは一般的な解決策ではない

std::vector<std::atomic<int>> v(3); 
v[0] = 1; 
v[1] = 2; 
v[2] = 3; 

オブジェクトは、適切なコンストラクタを呼び出すことによって得ることができるものに相当する構築後の突然変異を提供しない可能性があります。

ベクター構築で "emplace-like"の動作を得る方法はありますか?

+2

真剣に、私は 'std :: deque'を使うだけです。しかし、できない場合は、カスタムアロケータを使用して、必要なものを実行する唯一の方法があります。 – Brian

+0

@Brian - 'std :: deque'はこの構造イディオムを許可しますか? – BeeOnRope

+1

'std :: deque'では要素を一つずつ置き換えなければなりませんが、要素を先頭または末尾に追加しても他の要素は移動しないので機能します。 – Brian

答えて

1

一般的な解決方法は、適切な初期化を行うconstructメソッドを持つカスタムアロケータをベクトルにすることです。以下のコードでは、vstd::allocator<NonMovable>ではなくMyAllocator<NonMovable>アロケータを使用しています。引数なしでconstructメソッドが呼び出されると、実際には適切な引数を持つコンストラクタが呼び出されます。このようにして、デフォルトコンストラクタは要素を適切に初期化できます。

(simplicitlyために、私はこの例ではnext_value静的作られてきたが、それは全く同じようにMyAllocatorが構築されたときに初期化されています非静的メンバ変数である可能性があります。)

#include <stdio.h> 
#include <memory> 
#include <new> 
#include <vector> 

struct NonMovable { 
    NonMovable(int x) : x(x) {} 
    const int x; 
}; 

template <class T> 
struct MyAllocator { 
    typedef T value_type; 
    static int next_value; 
    T* allocate(size_t n) { 
     return static_cast<T*>(::operator new(n * sizeof(T))); 
    } 
    void deallocate(T* p, size_t n) { 
     ::operator delete(p); 
    } 
    template <class U> 
    void construct(U* p) { 
     new (p) U(++next_value); 
    } 
}; 

template <class T> int MyAllocator<T>::next_value = 0; 

int main() { 
    std::vector<NonMovable, MyAllocator<NonMovable>> v(10); 
    for (int i = 0; i < 10; i++) { 
     printf("%d\n", v[i].x); 
    } 
} 

http://coliru.stacked-crooked.com/a/1a89fddd325514bf

NonMovableクラスに触れることができず、そのコンストラクターに複数の引数が必要な場合は、これが唯一の解決策です。あなただけの各コンストラクタに1つの引数を渡す必要がある場合には、std::vectorの範囲のコンストラクタを使用していますので、よく似簡単な解決策があります:

std::vector<int> ints(10); 
std::iota(ints.begin(), ints.end(), 1); 
std::vector<NonMovable> v(ints.begin(), ints.end()); 

(けれども、あなたは余分なメモリを買う余裕ができない場合あなたはより多くのコードになるカスタムイテレータを書く必要があります)。

関連する問題