2017-07-16 75 views
0

考虑下面的代码:问题努力构建'的std :: VECTOR`与初始化列表

#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。为什么不编译?
  • 对于as2std::make_unique的结果是一个右值。为什么要在任何地方提交副本?
  • 有没有比我的as3更有地道或更短的方式来完成这项工作?

编辑:我现在记得as1中的错误原因。 Meyers'Effective Modern C++在第30条中提到了初始值设定项列表作为完美转发的失败情况之一:“将标准初始化程序传递给函数模板参数(未声明为std::initializer_list)被规定为,如标准所示,“未推断的上下文”。“

回答

2

问题是std::unique_ptr,而不是std::initializer_liststd::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)); 
} 

后者与可能抛出,如亨利在评论中指出,建设者的记忆,只剩安全解决方案。您应该在所有实际代码中使用后一个示例。

+1

“int”的例子在任何情况下都可以正常工作,因为'new int'只能抛出'bad_alloc',从中无法恢复。自定义数据类型的构造函数可能会抛出更多的问题,因为那样你会从原始指针的部分初始化向量中泄漏内存。 'emplace_back'解决方案是唯一的故障安全解决方案。 (1) –

3

as1

使独特的用途“完美的转发”。完美的转发是不完美的,并不能很好地支持初始化列表。

AS2

初始化列表是(双)指针的自动存储持续时间const阵列。 const对象不能从中移出,而是从中复制。你不能复制独特的ptrs。

AS3

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的帮助函数会削减样板。