我如何使用 emplace for unordered_set 将 shared_ptr 保存到对象?
How I can use emplace for unordered_set that holds shared_ptr to the object?
假设我有一个对象:
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)
看来我还需要一个自定义分配器,但我没有设法实现一个满足编译器要求的分配器。
我的问题是:我想要的是可能的。如果是,分配器如何是正确的方法。如果是的话,你能给我举个例子吗?
I receive compile error, because the constructor of Foo is obviously
not a constructor of std::shared_ptr. What I would like is when
emplace needs to construct a pointer to use
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));
It seems that I also need a custom allocator for that, but I did not
manage to implement one that satisfy the compiler.
如果您只想以最有效的方式添加 shared_ptr(通过在某些预分配元素上调用转发构造函数),那么自定义分配器完全是矫枉过正,否则我完全误解了你的问题。如果您不希望使用默认分配器(使用 operator new)构造元素,则通常会使用分配器。在这种情况下,shared_ptr 本身将是将在堆上构造的元素。如果您担心堆分配由于某种原因对您的目的来说效率低下(例如,如果您分配数百万个小对象),您只会使用分配器。
注意(正如@Yakk 所评论的),在这种情况下,shared_ptr 的实例化可能会抛出(我只能认为 bad_alloc 是可能的),在这种情况下传递给 emplace 的指针会导致泄漏。出于这个原因,我也认为 std::make_shared 会是一个更好的选择(如另一个答案中所述)。
您可以直接在 emplace
的参数列表中使用 std::make_shared
。
set.emplace(std::make_shared<Foo>(str1, str2));
不需要自定义分配器。
使用set.insert(std::make_shared<Foo>(str1, str2));
。 emplace
当重复键是一个问题时,对于具有唯一键的容器来说通常不是一个增益,因为它的操作方式:
- 它必须先构造对象,然后才能将它与容器中的现有键进行比较,以确定它是否可以插入。
- 一旦对象被构建,就不能被复制或移动,因为对象不需要是可复制或可移动的。此外,
emplace
没有可靠的方法来检测它何时 可以 复制或移动某些东西,因为 is_copy_constructible
returns 有很多类型true
但实际无法复制
- 对象构造只能发生一次,因为构造函数可能会从参数中移动或产生其他副作用。
emplace
的典型实现因此总是预先为节点分配内存,在该内存中构造对象,将其与容器中的现有元素进行比较,然后将其链接或销毁它并释放内存。
另一方面,insert
拥有随时可用的密钥。因此它可以先决定该节点是否应该被插入,只有在应该插入时才分配内存。
从理论上讲,对于 "one argument with the same type as the element type" 情况,实现可能是特殊情况 emplace
。但我知道没有实际执行此操作的实现。
假设我有一个对象:
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)
看来我还需要一个自定义分配器,但我没有设法实现一个满足编译器要求的分配器。
我的问题是:我想要的是可能的。如果是,分配器如何是正确的方法。如果是的话,你能给我举个例子吗?
I receive compile error, because the constructor of Foo is obviously not a constructor of std::shared_ptr. What I would like is when emplace needs to construct a pointer to use 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));
It seems that I also need a custom allocator for that, but I did not manage to implement one that satisfy the compiler.
如果您只想以最有效的方式添加 shared_ptr(通过在某些预分配元素上调用转发构造函数),那么自定义分配器完全是矫枉过正,否则我完全误解了你的问题。如果您不希望使用默认分配器(使用 operator new)构造元素,则通常会使用分配器。在这种情况下,shared_ptr 本身将是将在堆上构造的元素。如果您担心堆分配由于某种原因对您的目的来说效率低下(例如,如果您分配数百万个小对象),您只会使用分配器。
注意(正如@Yakk 所评论的),在这种情况下,shared_ptr 的实例化可能会抛出(我只能认为 bad_alloc 是可能的),在这种情况下传递给 emplace 的指针会导致泄漏。出于这个原因,我也认为 std::make_shared 会是一个更好的选择(如另一个答案中所述)。
您可以直接在 emplace
的参数列表中使用 std::make_shared
。
set.emplace(std::make_shared<Foo>(str1, str2));
不需要自定义分配器。
使用set.insert(std::make_shared<Foo>(str1, str2));
。 emplace
当重复键是一个问题时,对于具有唯一键的容器来说通常不是一个增益,因为它的操作方式:
- 它必须先构造对象,然后才能将它与容器中的现有键进行比较,以确定它是否可以插入。
- 一旦对象被构建,就不能被复制或移动,因为对象不需要是可复制或可移动的。此外,
emplace
没有可靠的方法来检测它何时 可以 复制或移动某些东西,因为is_copy_constructible
returns 有很多类型true
但实际无法复制 - 对象构造只能发生一次,因为构造函数可能会从参数中移动或产生其他副作用。
emplace
的典型实现因此总是预先为节点分配内存,在该内存中构造对象,将其与容器中的现有元素进行比较,然后将其链接或销毁它并释放内存。
insert
拥有随时可用的密钥。因此它可以先决定该节点是否应该被插入,只有在应该插入时才分配内存。
从理论上讲,对于 "one argument with the same type as the element type" 情况,实现可能是特殊情况 emplace
。但我知道没有实际执行此操作的实现。