【问题标题】:How to use emplace() in a std::map whose value is a std::set (map from something to a set)?如何在值为 std::set 的 std::map 中使用 emplace() (从某物映射到一个集合)?
【发布时间】:2015-04-22 10:35:45
【问题描述】:

问题

我有一个名为misistd::map<int, std::set<int>>。我想知道为什么misi.emplace(2345, {6, 9});misi.emplace({2345, {6, 9}}); 不能按预期工作,如下所示。

代码

#include <set>          // std:set
#include <map>          // std::map
#include <utility>      // std::piecewise_construct, std::pair
#include <tuple>        // std::forward_as_tuple
#include <iostream>     // std::cout, std::endl

int main()
{
    // --- std::set initializer list constructor ---
    std::set<int> si({42, 16});
    std::cout << "si.size(): " << si.size() << std::endl;       // 2
    std::cout << "*si.begin(): " << *si.begin() << std::endl;   // 16

    // --- std::set emplace() ---
    si.emplace(7);
    std::cout << "si.size(): " << si.size() << std::endl;       // 3
    std::cout << "*si.begin(): " << *si.begin() << std::endl;   // 7
    std::cout << "--------" << std::endl;

上面是std::set,可以看到初始化列表构造函数和emplace()完美运行。

    // --- std::map initializer list constructor ---
    std::map<int, int> mii({ {0, 42}, {1, 16} });
    std::cout << "mii.size(): " << mii.size() << std::endl;     // 2
    std::cout << "mii[0]: " << mii[0] << std::endl;             // 42
    std::cout << "mii[1]: " << mii[1] << std::endl;             // 16

    // --- std::map emplace() ---
    mii.emplace(1234, 7);
    std::cout << "mii.size(): " << mii.size() << std::endl;     // 3
    std::cout << "mii[1234]: " << mii[1234] << std::endl;       // 7

    // --- std::map emplace() with std::pair() ---
    mii.emplace(std::pair<int, int>(2345, 6));
    std::cout << "mii.size(): " << mii.size() << std::endl;     // 4
    std::cout << "mii[2345]: " << mii[2345] << std::endl;       // 6
    std::cout << "--------" << std::endl;

以上是intintstd::map,您可以看到这些方法也可以完美运行,除了在最后一个示例中,std::pair 可能在某种程度上是多余的。我想知道std::pair 是否就地构建。 (好吧,我猜不是)

    // --- std::map to std::set initializer list constructor ---
    std::map<int, std::set<int>> misi({ {0, {42, 16}}, {1, {7}} });
    std::cout << "misi.size(): " << misi.size() << std::endl;           // 2
    std::cout << "*misi[0].begin(): " << *misi[0].begin() << std::endl; // 16
    std::cout << "*misi[1].begin(): " << *misi[1].begin() << std::endl; // 7

对于std::mapstd::set,初始化列表构造函数如上所示完美运行。但是emplace() 没有! (如下图)

    // --- Compilation Errors ---
    //misi.emplace(2345, 6, 9);
    //misi.emplace({2345, 6, 9});
    //misi.emplace(2345, {6, 9});
    //misi.emplace({2345, {6, 9}});
    //misi.emplace(
    //  std::piecewise_construct,
    //  std::forward_as_tuple(2345),
    //  std::forward_as_tuple(6, 9)
    //);
    //misi.emplace(
    //  std::piecewise_construct,
    //  std::forward_as_tuple(2345),
    //  std::forward_as_tuple({6, 9})
    //);

这里,下面的语法是可以的,但并不完全符合我的要求:

    // --- OK ---
    misi.emplace(std::pair<int, std::set<int>>(2345, {6, 9}));
    std::cout << "misi.size(): " << misi.size() << std::endl;   // 3
    std::cout << "*misi[2345].begin(): " << *misi[2345].begin() << std::endl;   //6
    std::cout << "--------" << std::endl;

    return 0;
}

所以,似乎没有办法就地创建std::pair,而且似乎std::set 是就地创建的(对吧?)。有人有什么想法吗?

我使用的编译器是:

$ clang++ --version
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.4.0
Thread model: posix

【问题讨论】:

    标签: c++ c++11 stdmap stdset emplace


    【解决方案1】:

    带括号的初始化列表没有类型,所以它们不能被完美地转发。在这种特殊情况下,如果您希望所有内容都在适当的位置构建,您可以明确指定类型 (std::initializer_list&lt;int&gt;):

    misi.emplace(
      std::piecewise_construct,
      std::forward_as_tuple(2345),
      std::forward_as_tuple(std::initializer_list<int>{6, 9})
    );
    

    由于键和值只传递了一个参数,并且std::setinitializer_list构造函数不是explicit,因此您可以完全删除piecewise_construct并让emplace构造@987654328 @ 使用带有两个参数的构造函数:

    misi.emplace(
      2345,
      std::initializer_list<int>{6, 9}
    );
    

    mii.emplace(std::pair<int, int>(2345, 6));
    

    我想知道 std::pair 是否就地构造。 (嗯,我 猜不到)

    不,一个临时的std::pair&lt;int, int&gt; 被构造并传递给emplace,后者用它构造mapvalue_type(即pair&lt;const int, int&gt;)的一个实例。后者是实际存储在map 中的内容。


    misi.emplace(std::pair<int, std::set<int>>(2345, {6, 9}));
    

    似乎 std::set 是就地创建的(对吗?)。

    没有。同样,这会创建一个临时的std::pair&lt;int, std::set&lt;int&gt;&gt;,然后用它构造实际存储在map(即pair&lt;const int, std::set&lt;int&gt;&gt;)中的内容。第二个构造将从临时存储的set&lt;int&gt; 执行移动。

    【讨论】:

    • 非常感谢!我花了几个小时弄清楚如何去做!现在我使用 misi.emplace(2345, std::initializer_list&lt;int&gt;{6, 9}); 编译完美!
    • @SiuChingPong-AsukaKenji- 是的,很好,你实际上不需要在这里使用piecewise_construct
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-19
    • 2019-09-23
    • 2014-04-15
    • 2020-04-01
    • 1970-01-01
    • 2021-05-30
    • 1970-01-01
    相关资源
    最近更新 更多