我如何使用emplace来保存shared_ptr给对象的unordered_set?

时间:2015-12-06 03:47:17

标签: c++ c++11 stl unordered-set

说我有一个对象:

struct Foo {
    Foo(const std::string& str1, const std::string& str1) 
        : mStr1(str1), mStr2(str2)
    {}

    std::string mStr1;
    std::string mStr2;
};

并设置

typedef std::unordered_set<std::shared_ptr<Foo> , Hash, Compare> Set;

我有自定义哈希并进行比较。但是当我说:

Set set;
set.emplace(str1, str2);

我收到编译错误,因为Foo的构造函数显然不是std::shared_ptr<Foo>的构造函数。我想要的是当emplace需要构造一个使用std::make_shared<Foo>(str1, str2)

的指针时

似乎我还需要一个自定义分配器,但我没有设法实现满足编译器的分配器。

我的问题是:我想要的是什么。如果是,那么分配器是如何正确的。如果是的话,你能指点我一个例子。

3 个答案:

答案 0 :(得分:1)

使用set.insert(std::make_shared<Foo>(str1, str2));。对于具有唯一键的容器,emplace通常不是一个好处,因为重复键是一个问题,因为它的运行方式:

  • 它必须首先构造对象,然后才能将它与容器中的现有键进行比较,以确定它是否可以插入。
  • 构建对象后,无法复制或移动该对象,因为该对象不需要是可复制的或可移动的。此外,emplace没有可靠的方法来检测可以复制或移动某些内容,因为is_copy_constructible返回{{1}有很多类型但是无法实际复制。
  • 对象构造只能发生一次,因为构造函数可能会从参数移动或产生其他副作用。

true的典型实现因此总是为节点预先分配内存,构造该内存中的对象,将其与容器中的现有元素进行比较,然后将其链接,或者将其销毁,释放记忆。

另一方面,

emplace有密钥随时可用。因此,它可以首先决定是否应插入节点,并且只应分配内存。

理论上,实现可能是特殊情况insert用于&#34;一个参数类型与元素类型相同&#34;案件。但我知道实际上并没有实现这一点。

答案 1 :(得分:0)

  

我收到编译错误,因为Foo的构造函数很明显   不是std :: shared_ptr的构造函数。我想要的是什么时候   emplace需要构造一个使用指针   std :: make_shared(str1,str2)

emplace实现为一个函数,它使用完美转发来调用包含元素的构造函数(在本例中为shared_ptr)。包含的元素的构造函数接受一个指向Foo的指针,因此你应该能够这样做(就像你构造一个shared_ptr<Foo>对象一样):

set.emplace(new Foo("x", "y")); //or
set.emplace(new Foo(str1, str2)); 
  

似乎我还需要一个自定义分配器,但我没有   设法实现一个满足编译器的。

如果您想要做的就是以最有效的方式添加shared_ptr(通过在某些预分配元素上调用转发构造函数),或者我完全误解了您的问题,那么自定义分配器总是过度杀伤。如果您不希望使用默认分配器(使用operator new)构造元素,则通常会使用分配器。在这种情况下,shared_ptr本身将是将在堆上构造的元素。如果您担心堆分配由于某种原因而无法满足您的目的(例如,如果您分配了数百万个小对象),则只能使用分配器。

注意(正如@Yakk所评论的),在这种情况下,shared_ptr的实例化可能会抛出(我只能将bad_alloc视为可能),在这种情况下,传递给emplace的指针会导致泄漏。出于这个原因,我也认为std :: make_shared会是一个更好的选择(如另一个答案所述)。

答案 2 :(得分:0)

您可以直接在std::make_shared的参数列表中使用emplace

set.emplace(std::make_shared<Foo>(str1, str2));

无需自定义分配器。