2017-10-13 68 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()); 

(不过,如果您不能负担额外的内存,那么你将不得不编写一个自定义迭代器,这将是更多的代码。)