使用 GCC 4.7从初始化器列表初始化 only_ptrs 容器失败

我试图初始化一个 std::vector<std::unique_ptr<std::string>>的方式相当于 Bjarne Stroustrup's C++11 FAQ的一个例子:

using namespace std;
vector<unique_ptr<string>> vs { new string{"Doug"}, new string{"Adams"} }; // fails
unique_ptr<string> ps { new string{"42"} }; // OK

我看不出这种语法为什么会失败。这种初始化容器的方法有什么问题吗?
编译器的错误消息非常巨大; 我发现的相关片段如下:

/usr/lib/gcc-fast/lib/gcc/i686-linux-gnu/4.7.0/. ./. ./. ./. ./. ./include/c + +/4.7.0 /bits/stl_construct.h:77:7: error: no matching function for call to 'std::unique_ptr<std::basic_string<char> >::unique_ptr(std::basic_string<char>&)'

修复此错误的方法是什么?

28729 次浏览

在“修正”你的例子之后:

#include <vector>
#include <memory>
#include <string>


int main()
{
std::vector<std::unique_ptr<std::string>> vs = { { new std::string{"Doug"} }, { new std::string{"Adams"} } }; // fails
std::unique_ptr<std::string> ps { new std::string{"42"} }; // OK
}

我得到了一个非常明确的错误消息:

error: converting to 'std::unique_ptr<std::basic_string<char> >' from initializer list would use explicit constructor 'std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = std::basic_string<char>, _Dp = std::default_delete<std::basic_string<char> >, std::unique_ptr<_Tp, _Dp>::pointer = std::basic_string<char>*]'

这个错误告诉我们不可能使用 unique_ptr的显式构造函数!

unique_ptr's constructor is explicit. So you can't create one implicitly with from new string{"foo"}. It needs to be something like unique_ptr<string>{ new string{"foo"} }.

这就引出了这个问题

// not good
vector<unique_ptr<string>> vs {
unique_ptr<string>{ new string{"Doug"} },
unique_ptr<string>{ new string{"Adams"} }
};

但是,如果其中一个构造函数失败,它可能会泄漏。使用 make_unique更安全:

// does not work
vector<unique_ptr<string>> vs {
make_unique<string>("Doug"),
make_unique<string>("Adams")
};

但是... initializer_list总是执行拷贝,而且 unique_ptr是不可拷贝的。对于初始化器列表来说,这是一件非常烦人的事情。可以调用 黑进去,也可以通过调用 emplace_back回退到初始化。

如果您实际上使用智能指针管理 string,而且不仅仅是为了示例,那么您可以做得更好: 只要创建一个 vector<string>std::string已经处理了它所使用的资源。

这个问题现在有了更好的答案,至少在 C + + 17(C + + 11将需要更多的努力)。由于这是我在搜索“初始化急速向量”时得到的第一个 google 结果,所以我认为值得用一个解决方案来更新它。你可以使用可变参数函数来代替初始化列表。它是一个8行的粗糙的小实用程序函数,看起来像这样:

#include<memory>
#include<vector>
#include<type_traits>


template <class T> auto move_to_unique(T&& t) {
return std::make_unique<std::remove_reference_t<T>>(std::move(t));
}
template <class V, class ... Args> auto make_vector_unique(Args ... args) {
std::vector<std::unique_ptr<V>> rv;
(rv.push_back(move_to_unique(args)), ...);
return rv;
}

现在我们可以用一种直观的语法来构造一个向量:

auto vs = make_vector_unique<std::string>(std::string{"Doug"}, std::string{"Adam"});

您甚至可以使用它来创建派生类对象的向量。

class B {};
class D : public B {};


auto vb = make_vector_unique<B>(D{}, D{}, D{});