【问题标题】:C++ unique_ptr and mapC++ unique_ptr 和映射
【发布时间】:2011-04-23 19:21:52
【问题描述】:

我正在尝试在map 中使用C++0x unique_ptr class,如下所示:

// compile with `g++ main.cpp -std=gnu++0x`

#include <string.h>    
#include <map>
#include <memory>

using namespace std;

struct Foo {
    char *str;    
    Foo(char const *str_): str(strdup(str_)) {}
};

int main(void) {
    typedef std::map<int, unique_ptr<Foo>> Bar;
    Bar bar;
    auto a = bar.insert(Bar::value_type(1, new Foo("one")));
    return 0;
}

但是 GCC 给了我以下错误(缩短,我认为这是相关部分,请在您自己的 C++ 编译器上测试):

main.cpp:19:从这里实例化 /usr/include/c++/4.4/bits/unique_ptr.h:214: 错误: 删除函数‘std::unique_ptr::unique_ptr(const std::unique_ptr&) [with _Tp = Foo, _Tp_Deleter = std::default_delete]’ /usr/include/c++/4.4/bits/stl_pair.h:68:错误:在这里使用

我真的不确定我做错了什么,这适用于 MSVC。我发现了很多 similar 的问题,看起来很相似,但是他们的解决方案对我不起作用。

matt@stanley:/media/data/src/c++0x-test$ gcc --version gcc-4.4.real (Ubuntu 4.4.3-4ubuntu5) 4.4.3

【问题讨论】:

  • 不是答案,但 Foo 应该有一个释放 str 的析构函数,因为 strdup mallocs 内存并将 str_ 复制到该内存中。
  • 不管什么时候正式发布都是C++0x。正如 Stroustrup 所说,将 x 视为十六进制 :)
  • @Armen:谢谢你的快乐想法:D
  • @Loy Franco:是的,我知道,这将是下一个实施,但我从来没有做到这一点(这是我遇到的一个更大问题的简化摘录)。
  • 您应该添加一个析构函数 Foo::~Foo 来释放内存并明确禁用任何复制。

标签: c++ gcc c++11 stl unique-ptr


【解决方案1】:

首先:您的 Foo 类实际上是 std::string 的一个非常糟糕的近似值。我预测会有很多内存泄漏。 (搜索“三规则”。)

第二:指针不能隐式转换为 unique_ptr 对象(出于安全原因)。但是模板对构造函数要求参数可以隐式转换为相应的值类型。 GCC 似乎允许这样做,但这是一个错误。您必须手动创建 unique_ptr 对象。不幸的是,C++0x 草案缺少以下非常有用的函数模板,可以简化临时 unique_ptr 对象的创建。它也是异常安全的:

template<class T, class...Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
    std::unique_ptr<T> ret (new T(std::forward<Args>(args)...));
    return ret;
}

另外,我们用新的emplace函数代替insert

auto a = bar.emplace(1,make_unique<Foo>("one"));

emplace 将两个参数转发给相应的对构造函数。

您目睹的实际问题是,pair 构造函数试图复制一个 unique_ptr,即使这样的对象只是“可移动的”。似乎 GCC 的 C++0x 支持还不足以正确处理这种情况。据我所知,我上面的线路应该符合当前的标准草案(N3126)。

编辑:我刚刚在实验性 C++0x 模式下使用 GCC 4.5.1 尝试了以下解决方法:

map<int,unique_ptr<int> > themap;
themap[42].reset(new int(1729));

这也应该适用于即将发布的标准,但 GCC 也拒绝它。看来您必须等待 GCC 在地图和多地图中支持 unique_ptr。

【讨论】:

  • @Matt:好吧,对此无能为力。 std::unique_ptr 作为映射中的映射类型应该可以工作。 GCC 还不能处理这个问题。您可以提交错误报告。
  • 感谢您的精彩回答。在 GCC 中添加支持后,我将能够对其进行测试并给予 +1!
  • 我也遇到了这个问题。感谢您的详细撰写。为我省了很多麻烦。
  • 这似乎是Clang 3.6.2 的问题
【解决方案2】:

我不相信在 map::insert 中正确使用 unique_ptr 是可能的。这是它的 GCC 错误:

Bug 44436 - [C++0x] Implement insert(&amp;&amp;) and emplace* in associative and unordered containers

它看起来可能是fixed for GCC 4.6,但它不会在 vanilla Ubuntu 10.04.1 上从 SVN 构建来确认。

更新0

这不适用于 GCC svn r165422 (4.6.0)。

【讨论】:

  • 有问题的代码与 GCC 4.6.1 一起工作,但稍作更改(添加了对 unique_ptr&lt;Foo&gt; 的显式转换)。
【解决方案3】:

我刚刚在 gcc 4.6.0 中修改了 unique_ptr。这是一些(非常丑陋的)可以正常工作的代码:

std::map<int, std::unique_ptr<int> > table;
table.insert(std::pair<int, std::unique_ptr<int>>(15, std::unique_ptr<int>(new int(42))));
table[2] = std::move(table[15]);
for (auto& i : table)
{
    if (i.second.get())
        printf("%d\n", *i.second);
    else
    {
        printf("empty\n");
        i.second.reset(new int(12));
    }
}
printf("%d\n", *table[15]);

输出是 42,空,12 - 显然它有效。据我所知,唯一的“问题”是那个看起来很糟糕的插入命令。 'emplace' 在 gcc 4.6.0 中没有实现

【讨论】:

  • +1 至少插入并且该对的转换构造函数似乎按预期工作(转换因为value_typepair&lt;const int,....)但是w.r.t 仍然存在小问题。异常安全。通常,动态分配对象并使用该指针初始化 unique_ptr 应该是连续的操作。但是 C++ 不保证子表达式的求值顺序。这就是我使用 make_unique 作为函数调用的原因。这样,有两个额外的序列点。
【解决方案4】:

尝试使用emplace的map方法。

【讨论】:

    猜你喜欢
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    • 2012-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-28
    相关资源
    最近更新 更多