C++ std::vector的类侵位构造

C++ std::vector的类侵位构造,c++,vector,emplace,C++,Vector,Emplace,假设我想要构造一个固定大小的对象std::vector,而不使用移动或复制构造函数,例如std::atomic。在这种情况下,底层的std::atomic类有一个1-arg构造函数,它接受int,还有一个默认构造函数(它将值初始化为0) 使用initializer\u list语法,如std::vector v{1,2,3}不起作用,因为在创建initializer\u list时,参数首先转换为向量的元素类型t,因此将调用复制或移动构造函数 在std::atomic的特定情况下,我可以默认构造

假设我想要构造一个固定大小的对象
std::vector
,而不使用移动或复制构造函数,例如
std::atomic
。在这种情况下,底层的
std::atomic
类有一个1-arg构造函数,它接受
int
,还有一个默认构造函数(它将值初始化为0)

使用
initializer\u list
语法,如
std::vector v{1,2,3}
不起作用,因为在创建
initializer\u list
时,参数首先转换为向量的元素类型
t
,因此将调用复制或移动构造函数

std::atomic
的特定情况下,我可以默认构造向量,然后在以下情况下对元素进行变异:

std::vector<std::atomic<int>> v(3);
v[0] = 1;
v[1] = 2;
v[2] = 3;
std::vectorv(3);
v[0]=1;
v[1]=2;
v[2]=3;
然而,除了丑陋和低效之外,它不是一个通用的解决方案,因为许多对象可能不会提供与调用适当的构造函数所能得到的等价的构造后变异


有什么方法可以在向量构造中获得我想要的“类似于安放”的行为吗?

一般的解决方案是让你的向量接受一个自定义分配器,它的
构造方法执行适当的初始化。在下面的代码中,
v
使用
MyAllocator
分配器,而不是
std::allocator
。当调用
construct
方法时没有参数,它实际上使用适当的参数调用构造函数。通过这种方式,默认构造函数可以正确初始化元素

(简单地说,在本例中,我将
next\u值设置为static,但它也可以是在构造
MyAllocator
时初始化的非静态成员变量。)


(虽然如果你负担不起额外的内存,那么你必须编写一个自定义迭代器,这将需要更多的代码。)

说真的,我只会使用
std::deque
。但是如果你不能,唯一能做你想做的事情的方法就是通过一个自定义的分配器。@Brian-是否允许这种构造习惯用法?使用
std::deque
你必须一个接一个地放置元素,但它会起作用,因为在开始或结束添加元素不会移动任何其他元素。@Brian-对,在这方面,
std::vector
至少更好,不幸的是,有时我需要
vector
提供的连续存储保证。我还对自定义分配器方法感兴趣:自定义分配器如何绕过
vector
构造函数接口对我来说并不明显
#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);
    }
}
std::vector<int> ints(10);
std::iota(ints.begin(), ints.end(), 1);
std::vector<NonMovable> v(ints.begin(), ints.end());