C++ 尝试使用初始值设定项列表构造'std::vector'时出现问题

C++ 尝试使用初始值设定项列表构造'std::vector'时出现问题,c++,c++11,vector,initializer-list,C++,C++11,Vector,Initializer List,考虑以下代码: #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:

考虑以下代码:

#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})));
}
#包括
#包括
甲级{
公众:
显式A(std::vector&&v):v(std::move(v)){
私人:
std::向量v_;
};
int main(){
//编译错误(没有对std::make_unique的匹配调用)
//编译器输出:https://ideone.com/4oKjCS
std::vector as1={std::make_unique({1}),
std::使_唯一({2}});
//编译错误(请求的std::unique\u ptr副本)
//编译器输出:https://ideone.com/5LGPoa
标准::向量as2={
std::make_unique(std::vector({1})),
std::make_惟一(std::vector({2}))};
//成功
std::载体as3;
as3.push_-back(std::make_-unique(std::vector({1}));
as3.push_-back(std::make_-unique(std::vector({2}));
}
  • 对于
    as1
    :我希望
    std::make_unique({1})
    调用
    std::vector
    的隐式初始值设定项列表构造函数,然后将向量传递给
    std::make_unique
    。为什么不编译
  • 对于
    as2
    :std::make_unique的结果为右值。为什么在任何地方都需要副本
  • 有没有比我的
    as3
    更惯用或更短的方法来实现这一点

编辑:我现在想起了
as1
中错误的原因。迈尔斯有效的现代C++提到了项目30中的初始化列表,作为完美转发的失败案例之一:“将一个支持的初始化器传递给一个未被声明为的函数模板参数:按照标准称为“非推导上下文”。问题在于
std::unique\u ptr
,而不是
std::initializer\u列表
std::initializer\u list
中的值通过临时缓冲区复制到目标对象<代码>唯一\u ptr不可复制。您需要以另一种方式初始化它,可能是通过
reserve()/emplace\u 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;
}

正如Henri在评论中指出的那样,后者是唯一具有可能抛出的构造函数的内存安全解决方案。您应该在所有实际代码中使用后一个示例。

问题在于
std::unique\u ptr
,而不是
std::initializer\u列表。
std::initializer\u list
中的值通过临时缓冲区复制到目标对象<代码>唯一\u ptr
不可复制。您需要以另一种方式初始化它,可能是通过
reserve()/emplace\u 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;
}
正如Henri在评论中指出的那样,后者是唯一具有可能抛出的构造函数的内存安全解决方案。您应该在所有实际代码中使用后一个示例。

as1 独特使用“完美转发”。完美转发是不完美的,并且不能很好地支持初始值设定项列表

as2 初始值设定项列表是指向自动存储持续时间
const
数组的(成对)指针<代码>常量对象不能从中移动,而是从中复制。不能复制唯一的PTR

as3
模板
标准::向量生成向量(Ts&&…Ts){
std::array tmp={{std::forward(ts)…};
std::vsctor r{
std::make_move_迭代器(begin(tmp)),
std::make_move_迭代器(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))
);
auto as4=生成向量(
std::make_unique(make_向量(1)),
标准::使_唯一(使_向量(2))
);
这可能并不理想,但对向量周围的薄包装对象进行独特的ptr是个坏主意

在更复杂的情况下,直接生成唯一a的辅助函数将简化样板文件。

as1 独特使用“完美转发”。完美转发是不完美的,并且不能很好地支持初始值设定项列表

as2 初始值设定项列表是指向自动存储持续时间
const
数组的(成对)指针<代码>常量对象不能从中移动,而是从中复制。不能复制唯一的PTR

as3
模板
标准::向量生成向量(Ts&&…Ts){
std::array tmp={{std::forward(ts)…};
std::vsctor r{
std::make_move_迭代器(begin(tmp)),
std::make_move_迭代器(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))
);
auto as4=生成向量(
std::make_unique(make_向量(1)),
标准::使_唯一(使_向量(2))
);
这可能并不理想,但对向量周围的薄包装对象进行独特的ptr是个坏主意


在更复杂的情况下,直接生成唯一a的帮助函数将简化样板文件。

带有
int
的示例在任何情况下都可以正常工作,因为
new int
只能抛出
bad\u alloc
,您无论如何都无法从中恢复。对于构造函数可能抛出的自定义数据类型,问题要大得多,因为这样会从原始指针的部分初始化向量中泄漏内存。
emplace\u back
解决方案是唯一的故障安全解决方案。(+1)带有
int
的示例在任何情况下都可以正常工作,因为
new i