【问题标题】:Initializing container of unique_ptrs from initializer list fails with GCC 4.7GCC 4.7 从初始化器列表初始化 unique_ptrs 容器失败
【发布时间】:2012-03-25 23:19:35
【问题描述】:

我正在尝试以与Bjarne Stroustrup's C++11 FAQ 中的示例等效的方式初始化std::vector<std::unique_ptr<std::string>>

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-snapshot/lib/gcc/i686-linux-gnu/4.7.0/../../../../include/c++/4.7.0 /bits/stl_construct.h:77:7:错误:没有匹配的函数调用 'std::unique_ptr&lt;std::basic_string&lt;char&gt; &gt;::unique_ptr(std::basic_string&lt;char&gt;&amp;)'

有什么方法可以解决这个错误?

【问题讨论】:

  • 它正在拾取输入迭代器 ctor
  • @PlasmaHH 在我的实际代码中,初始化列表中有很多条目,所以我不认为这是问题所在。
  • @juanchopanza:这是您在此处粘贴的代码中的问题,您可以通过追溯实例化跟踪轻松查看。当然,我们不能对你没有在这里展示的代码说什么。
  • @PlasmaHH 这很合理。粘贴的代码是我引用的 C++11 FAQ 中的示例。但是,如果我删除 unique_ptrs 并使用裸字符串指针,则两个参数初始化器列表可以正常工作。

标签: c++ compiler-errors initialization c++11


【解决方案1】:

unique_ptr 的构造函数是explicit。所以你不能用new string{"foo"} 隐式创建一个。它需要类似于unique_ptr&lt;string&gt;{ 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_lists 总是执行复制,而unique_ptrs 是不可复制的。这对于初始化列表来说真的很烦人。您可以hack around it,或通过调用emplace_back 回退到初始化。

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

【讨论】:

  • 链接+1!不,我实际上并不关心将 unique_ptrs 的向量制作成字符串。这只是常见问题解答中的示例。
  • 第一个例子不会泄露。初始值设定项列表中的, 是一个序列点(在新的、更麻烦的术语中,在{ a, b } 中,ab 之前排序)。参见 C++11 §8.5.4/4。不过,使用make_unique 仍然是个好主意。
  • std::initializer_list 有什么理由不能像其他任何对象一样具有移动语义吗?
【解决方案2】:

在“修复”您的示例之后:

#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的显式构造函数!

【讨论】:

  • Aaaand... 该错误消息什么也没告诉我们。 为什么不能使用显式ctor?
  • @Xeo - 该工作的哪一点明确调用了ctor?那里可能有一个隐式调用,但这是被禁止的。
  • unique_ptr 的语义是它不能被复制,这就是构造函数被删除的原因。但是std::vector 不应该具有移动感知能力,从而能够从临时移动到向量吗?
  • @drhirch 不能复制构造,但可以从字符串显式构造。所以错误是这里尝试的是来自字符串指针的隐式构造。
  • @VJovic 我认为错误是不可能从字符串指针隐式构造 unique_ptr,所以我会说“错误表明不可能隐式构造 unique_ptr,因为构造函数是显式的。你同意吗?
猜你喜欢
  • 1970-01-01
  • 2015-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多