类似于emplace的std::vector构造方式

3
假设我想构造一个固定大小的std :: vector对象,该对象没有移动或复制构造函数,例如std :: atomic。 在这种情况下,底层的std :: atomic类具有一个接受int类型的参数的1个参数构造函数,以及一个默认构造函数(它将值初始化为0)。
使用initializer_list语法,例如std :: vector > v {1,2,3}是行不通的,因为在创建initializer_list时,参数首先转换为向量的元素类型T,因此将调用复制或移动构造函数。
对于std :: atomic 的特殊情况,我可以默认构造向量,然后在其后改变元素。
std::vector<std::atomic<int>> v(3);
v[0] = 1;
v[1] = 2;
v[2] = 3;

然而,除了不美观且效率低下外,它并不是一个通用的解决方案,因为许多对象可能没有提供与调用适当的构造函数等效的后构建突变。

有没有办法在向量构造时获得我想要的"emplace-like"行为?


2
严肃的说,我只会使用 std::deque。但如果你不能用,想实现你想要的唯一方法是通过自定义分配器。 - Brian Bi
@Brian - std::deque允许这种构造习惯用法吗? - BeeOnRope
1
使用 std::deque 时,您需要逐个插入元素,但这是可行的,因为在开头或结尾添加元素不会移动任何其他元素。 - Brian Bi
@Brian - 是的,至少在这方面上它比std::vector更好,但不幸的是有时我需要vector提供的连续存储的保证。我也对自定义分配器方法感兴趣:对我来说,自定义分配器如何解决vector构造函数接口并不明显。 - BeeOnRope
在我看来,这似乎是与 https://dev59.com/w-o6XIcBkEYKwwoYPR7f 相同的问题,只是措辞有些不同。 - M.M
显示剩余2条评论
1个回答

2
一般的解决方案是让您的向量采用自定义分配器,其construct方法执行适当的初始化。在下面的代码中,v使用MyAllocator<NonMovable>分配器,而不是std::allocator<NonMovable>。当没有参数调用construct方法时,它实际上会调用具有适当参数的构造函数。通过这种方式,默认构造函数可以正确地初始化元素。
(为了简单起见,在此示例中,我已将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());
< p >(如果您无法承担额外的内存,那么您将必须编写自定义迭代器,这将需要更多的代码。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接