想象一下,我想构建一个固定大小的std::vector
个对象,而不需要移动或复制构造函数,例如std::atomic<int>
。在这种情况下,底层std::atomic
类有一个1-arg构造函数,它带有int
,以及一个默认构造函数(将值初始化为0)。
使用像initializer_list
这样的std::vector<std::atomic<int>> v{1,2,3}
语法不起作用,因为参数首先转换为向量的元素类型T
,作为创建{{1}的一部分因此将调用复制或移动构造函数。
在initializer_list
的特定情况下,我可以默认构造向量,然后在以下后改变元素:
std::atomic<int>
然而,除了丑陋和低效之外,它不是一般解决方案,因为许多对象可能不会提供与通过调用适当的构造函数所能获得的相同的构造后变异。
有没有办法在矢量构造中获得我想要的“类似于emplace”的行为?
答案 0 :(得分: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());
(虽然如果你买不起额外的内存,那么你将不得不编写一个自定义迭代器,这将是更多的代码。)