如何最好地将 emplace 与 std::map 一起使用



我正在尝试使用std::map::emplace()而不是插入。我在下面有一个简单的测试程序:

#include <iostream>
#include <map>
#include <string>
class CTest
{
public:
CTest() : Value(0), Name() { std::cout << "Default constructor" << std::endl; };
CTest(int val, const std::string &name) : Value(val), Name(name) {
std::cout << "Parameterized constructor" << std::endl; }
CTest(const CTest& test) {
Value = test.Value; Name = test.Name;  std::cout << "Copy Constructor" << std::endl; }
CTest(CTest&& test) noexcept {
Value = test.Value; Name = test.Name; std::cout << "Move Constructor" << std::endl; }
CTest& operator=(const CTest& test) {
Value = test.Value; Name = test.Name; std::cout << "Copy assignment" << std::endl; return *this; }
CTest& operator=(CTest &&test) noexcept {
Value = test.Value; Name = test.Name; std::cout << "Move assignment" << std::endl; return *this; }
~CTest() { std::cout << "Destructor" << std::endl; }

private:
int Value;
std::string Name;
};
int main()
{
CTest t1(1, "hello");
CTest t2(2, "hello");

std::map<int, CTest> testMap;
testMap[1] = t1;         //1
testMap.emplace(2, t2);  //2
testMap.emplace(3, CTest(3, "hello"));  //3
testMap.emplace(std::piecewise_construct, std::forward_as_tuple(4), std::forward_as_tuple(4, "hello"));    //4
testMap.emplace(std::piecewise_construct, std::forward_as_tuple(4), std::forward_as_tuple(std::move(t1))); //5
return 0;
}

每个输出为:


1 默认构造函数
复制赋值

阿拉伯数字
复制构造函数


3参数化构造函数 移动构造函数

析构函数


四参数化构造函数


5移动构造函数析构函数

1 涉及最多的复制:使用默认构造函数在映射中创建一个条目,然后是复制赋值。我很惊讶地看到析构函数调用 3 和 5。在这两种情况下,传递的值都是右值。那么,是否从传入的右值临时创建,使用后删除?这就引出了一个问题,使用emplace的正确方法是什么?你应该只传递构造函数的参数,就像在 4 中一样吗?正如我的结果所显示的那样,这是最好的性能。

你应该只传递构造函数的参数吗

是的,因为这就是所有emplace()功能的设计目的。使用insert(),您必须构造一个对象,然后 [通常] 将其复制到容器中。通常,如果您使用的是容器,则只是在构造,以便将它们放入容器中。正如您在测试中看到的那样,这是一些额外的工作。

emplace()旨在允许您直接在容器中构建。您可以通过向 emplace 函数提供构造函数参数来实现这一点。 如果您已经有一个对象并希望将其放入容器中,则使用insert()

我有一个尖刻的评论,其他人已经注意到值得多解释一下。如果你的类(我称之为Foo(具有单参数构造函数,那么看起来你可以做与emplace()相同的事情,只需将单个参数传递给insert()push_back()之类的东西,或者任何将Foo作为参数的地方。这是语言的一个"功能",编译器将隐式为您构造一个Foo并使用它。问题是,在引擎盖下,它并没有做同样的事情。emplace()将直接在容器中生成对象,利用单个参数构造函数伪造它仍会导致创建副本。要考虑的另一个缺点是这种隐式转换。它可能会损害代码的可读性,或者更糟的是,破坏东西。这可以通过将构造函数标记为explicit来避免。

最新更新