请考虑以下代码:
#include <memory>
#include <vector>
class A {
public:
explicit A(std::vector<int> &&v) : v_(std::move(v)) {}
private:
std::vector<int> v_;
};
int main() {
// compilation error (no matching call to std::make_unique)
// compiler output: https://ideone.com/4oKjCS
std::vector<std::unique_ptr<A>> as1 = {std::make_unique<A>({1}),
std::make_unique<A>({2})};
// compilation error (requested copy of std::unique_ptr)
// compiler output: https://ideone.com/5LGPoa
std::vector<std::unique_ptr<A>> as2 = {
std::make_unique<A>(std::vector<int>({1})),
std::make_unique<A>(std::vector<int>({2}))};
// succeeds
std::vector<std::unique_ptr<A>> as3;
as3.push_back(std::make_unique<A>(std::vector<int>({1})));
as3.push_back(std::make_unique<A>(std::vector<int>({2})));
}
as1
:我希望std::make_unique<A>({1})
调用std::vector
的隐式初始化列表构造函数,然后将向量传递给std::make_unique
。为什么不编译?as2
:std::make_unique
的结果是右值。为什么要在任何地方要求复制?as3
相比,是否有更惯用或更短的方法来完成此工作?编辑:我现在正在记住as1
中出错的原因。迈尔斯&#39; Effective Modern C ++ 在第30项中提到初始化列表作为完美转发的失败案例之一:&#34;将支持的初始化程序传递给未声明为std::initializer_list
的函数模板参数正如标准所说,法令规定是一个非推断的背景。&#39;&#34;
答案 0 :(得分:3)
独特使用&#34;完美转发&#34;。完美转发是不完美的,并且不能很好地支持初始化列表。
初始化列表是指向自动存储持续时间const
数组的(成对)指针。 const
个对象无法移动,而是从中复制。你不能复制独特的ptrs。
template<class T, class...Ts>
std::vector<T> make_vector(Ts&&...ts){
std::array<T,sizeof...(ts)> tmp={{std::forward<Ts>(ts)...}};
std::vsctor<T> r{
std::make_move_iterator(begin(tmp)),
std::make_move_iterator(end(tmp))
};
}
给我们:
auto as4=make_vector<std::unique_ptr<A>>(
std::make_unique<A>(make_vector<int>(1)),
std::make_unique<A>(make_vector<int>(2))
);
这可能不太理想,但是一个独特的对象ptr是一个很糟糕的概念。
在更复杂的情况下,直接生成唯一A的辅助函数会减少样板。
答案 1 :(得分:2)
问题是std::unique_ptr
,而不是std::initializer_list
。来自std::initializer_list
的值通过临时缓冲区复制到目标对象。 unique_ptr
不可复制。您需要以另一种方式初始化它,可能是通过reserve()/emplace_back()
。
对不起,我知道这听起来令人生气,但确实有没有好的方法来为此目的使用初始化列表。
以下示例显示了如何使用带有初始化列表的原始指针的临时向量。这个例子并不漂亮,我不推荐它用于任何真正的代码,但是如果你设置在初始化列表上,它将与std::unique_ptr
一起使用,并且只要构造函数没有引入内存泄漏不要扔。
#include <memory>
#include <vector>
int main(void)
{
std::vector<int*> v = {
new int(1),
new int(2),
new int(3),
new int(4),
};
std::vector<std::unique_ptr<int>> v1(v.begin(), v.end());
return 0;
}
相反,我会推荐更类似于原始示例的内容:使用reserve
/ emplace_back()
。也许更冗长,但目的很明确,而且语法更加惯用。
std::vector<std::unique_ptr<int>> v;
v.reserve(50);
for (size_t i = 0; i < 50; ++i) {
v.emplace_back(std::make_unique<int>(i));
}
正如Henri在评论中指出的那样,后者是唯一可以抛出构造函数的内存安全解决方案。您应该在所有实际代码中使用后一个示例。