2017-10-13 112 views
3

想象一下,我想構建一個沒有移動或複製構造函數的固定大小的std::vector,例如std::atomic<int>。在這種情況下,底層的std::atomic類有一個1-arg構造函數,它接受一個int以及一個默認構造函數(將值初始化爲0)。std :: vector的安置式構造

使用initializer_list語法等std::vector<std::atomic<int>> v{1,2,3}不起作用,因爲參數首先被轉換爲向量的元素T類型創建initializer_list的的一部分,因此所述複製或移動的構造將被調用。

std::atomic<int>的特定情況下,我可以缺省方式構造的載體中,然後發生變異的元素後:

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

然而,除了是難看和低效的,這不是因爲許多一般的溶液對象可能不會提供相當於通過調用相應的構造函數可以獲得的構造後變化。

有沒有什麼辦法可以獲得我想要在矢量構建中使用的「類似於emplace的」行爲?

+2

說真的,我只是使用'std :: deque'。但是如果你不能,唯一的辦法就是通過自定義分配器來做你想做的事情。 – Brian

+0

@布萊恩 - 'std :: deque'是否允許這種構造方式? – BeeOnRope

+1

對於'std :: deque',你必須逐個放置元素,但是它會起作用,因爲向開始或結束添加元素不會移動任何其他元素。 – Brian

回答

1

一個通用的解決方案是讓你的矢量採用自定義分配器,其construct方法執行適當的初始化。在下面的代碼中,v使用MyAllocator<NonMovable>分配器而不是std::allocator<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類時,其構造函數可能需要多個參數。在你只需要一個參數傳遞給每個構造的情況下,存在使用的std::vector範圍內的構造,像這樣一個更簡單的解決方案:

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

(不過,如果您不能負擔額外的內存,那麼你將不得不編寫一個自定義迭代器,這將是更多的代碼。)