emplace(std::move(key), std::move(value))与emplace(std::make_pair(key, value))的区别

3

两种嵌入方式:

std::unordered_map<std::string, std::string> m;

首先:emplace移动键和值。
// 1.
{
    std::string k1 = "key1";
    std::string v1 = "value1";
    m.emplace(std::move(k1), std::move(v1));
}

第二点:使用由 std::make_pair 创建的pair进行emplace操作:
// 2.
{
    std::string k2 = "key2";
    std::string v2 = "value2";
    m.emplace(std::make_pair(k2, v2));
}

哪个更好(指更快速高效)?

1
请注意,第2点大致相当于 m.insert(std::make_pair(k2, v2)); ,因为 insert 被重载以接受右值引用。 - Chris Drew
2
它们在语义上是不同的。第一个可能会使 k1k2 无效。在 std::move 之后,您不应该依赖它们的值。第二个将始终使它们有效,因为它们是左值,并且 std::make_pair 的参数类型将解析为 std::string&。原因是 std::make_pair 接受所谓的通用引用,即在类型推导上下文中的右值引用。 - Sergey
我认为字符串 k1v1 是否被移动是一个分散注意力的问题,而本来这是一个很好的问题。在两种情况下,您都可以选择移动或不移动字符串(并且使用SSO对性能影响很小)。我认为将同类进行比较会更清晰明了。 - Chris Drew
2个回答

2

哪个更好?

更好的是什么?它们并不等价。

示例1调用了k1v1的移动构造函数,使它们处于有效但不同的状态,它们的内容将被移走。如果您打算在之后使用k1v1,那么没有选择,您应该使用#2。如果您不打算使用它们,那么您也应该在#2中移动以避免复制:

m.emplace(std::make_pair(std::move(k2), std::move(v2)));

这就让可读性成为了一个问题:

m.emplace(std::move(k1), std::move(k2));

这使用std::pair的模板构造函数,从两个右值引用构造std::pair

与之对比:

m.emplace(std::make_pair(std::move(k2), std::move(v2)));

这会调用std::pair的移动构造函数,因为它是右值。
如果你的意思是"哪个更好?"指的是性能,那么只有一种方法可以找出,那就是基准测试。虽然我认为m.emplace(std::move(k1), std::move(k2));稍微占优势。(正如评论中所指出的,因为它避免了从非const到const的转换)
主要观点是,不要太担心,它们都会非常高效,因为所有内容都在移动。选择你认为更易读的那一个。

2
什么微小的优势? - arturx64
2
但是使用 make_pair 也会将 pair<string, string> 转换为 pair<const string, string>。如果您使用 std::unordered_map<std::string, std::string>::value_type 构造函数而不是 make_pair,则情况可能会再次有所不同。 - j6t
你是正确的。std::pair<const string, string> 应该避免额外的构造。 - arturx64
@j6t 你说得没错,我在谈到“微小优势”时复制了错误的内容。已经修正,谢谢。 - Hatted Rooster

0

第二个选项需要额外的移动/复制操作,这是不好的。第一个选项工作速度更快,因为没有显式对创建配对。实际上,使用显式创建 std::pair 的插入和放置会给你相同的速度。
我已经测试过了。使用 std::make_pair 的选项需要调用 3 次键值的移动构造函数,而使用 std::move 的选项只需要调用一次移动构造函数。 示例中包含有 /Ox 优化和没有优化的版本。msvc-2012

class C
{
    public:
    C()
    {
       std::cout << "C()" << std::endl;
    }

    C( const C& c )
    {
       std::cout << "copy" << std::endl;
    }

    C( C&& c )
    {
       std::cout << "move" << std::endl;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    std::unordered_map<int, C> c1;

    std::cout << "---Test Move---" << std::endl;
    C test1;
    c1.emplace(1, std::move(test1));

    std::cout << "---Test std::make_pair---" << std::endl;
    C test2;
    c1.emplace(std::make_pair(2, test2) );

    std::cout << "---Test bare pair---" << std::endl;
    C test3;
    c1.emplace(std::pair<const int, C>(3, test3) );

    return 0;
}

---测试移动操作---
C()
移动
---测试 std::make_pair---
C()
复制
移动
移动
---测试裸对(pair)---
C()
复制
移动


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接