【问题标题】:How to emplace object with no-argument constructor into std::map?如何将具有无参数构造函数的对象放置到 std::map 中?
【发布时间】:2014-12-18 18:59:28
【问题描述】:

我想将一个对象放置到 std::map 中,其构造函数不接受任何参数。但是,std::map::emplace 似乎除了键之外至少需要一个额外的参数。那么如何将零参数转发给构造函数呢?

【问题讨论】:

    标签: c++ c++11


    【解决方案1】:

    std::map<K, V> 的元素类型实际上是std::pair<K, V>,所以当你放入地图时,参数将被转发给std::pair 的构造函数。这就是为什么你不能只传递键:std::pair<K, V> 不能从单个参数构造(除非它是另一对相同类型。)你可以传递零个参数,但是键将被初始化,这可能不是你想要的。

    在大多数情况下,移动值会很便宜(并且键会很小且可复制),您真的应该这样做:

    M.emplace(k, V{});
    

    其中V 是映射类型。它将被值初始化并移动到容器中。 (这个动作甚至可能被忽略;我不确定。)

    如果你不能移动,而你确实需要就地构造V,你必须使用分段构造构造函数...

    M.emplace(std::piecewise_construct, std::make_tuple(k), std::make_tuple());
    

    这会导致std::pair 使用k 构造第一个元素,使用零参数(值初始化)构造第二个元素。

    【讨论】:

    • 为什么不用forward_as_tuple 而不是make_tuple
    • @Yakk 好吧,在这种情况下,为了节省一点打字。显然有些情况需要forward_as_tuple
    • 请注意,简单的M[k];piecewise_construct 的效果相同。
    • M[k] 返回一个引用;有时获取迭代器会很有用。
    • @Yakk 对我来说,只有make_tuple 适用于不可移动的类型。 forward_as-tuple 也会尝试移动,这对我的班级来说是不可能的(故意的)。 @Brian 在这里救了我。
    【解决方案2】:

    您可以显式创建pair 并将其传递给map::emplace,或者使用std::pairpiecewise construction constructor

    struct foo {};
    
    std::map<int, foo> m;
    
    m.emplace(std::pair<int, foo>(1, {}));
    m.emplace(std::piecewise_construct,
              std::forward_as_tuple(2),
              std::forward_as_tuple());
    

    Live demo

    【讨论】:

      【解决方案3】:

      当我必须创建 std::mapstd::mutex 对象时,我遇到了同样的问题。问题是std::mutex 既不能复制也不能移动,所以我需要“就地”构建它。

      接受的答案不适用于这种情况(M.emplace(k, V{}); 需要 V 是可移动的)。而且我不想使用复杂且可读性较差的std::piecewise_construct 选项(请参阅上面的其他答案)。

      我的解决方案要简单得多——只需使用operator[]——它将使用其默认构造函数创建值并返回对它的引用。或者它只会查找并返回对现有项目的引用而不创建新项目。

      std::map<std::string, std::mutex> map;
      
      std::mutex& GetMutexForFile(const std::string& filename)
      {
          return map[filename]; // constructs it inside the map if doesn't exist
      }
      

      【讨论】:

      • 并不是我在敲这个答案,但显然,如果您的对象没有默认构造函数(构造函数不带参数),这将不起作用。在这种情况下,您必须使用 try_emplace(仅限 C++17)或 emplace,详见其他答案。另请注意,此答案在视觉上更清晰,但速度稍慢(仅在您大量执行此操作时才真正重要),因为它(即operator[])必须检查对象是否存在,因此它可以知道是否返回它找到的东西或先在那里创建一个新对象。
      • @Keith M 最初的问题是关于默认构造函数的。
      • @DevNull 就像我说的,我不是在敲答案。然而,谷歌搜索简单的“std map emplace constructor arguments”会出现这个问题,所以我认为我的评论对像我这样需要使用> = 2个参数的人会有所帮助。毕竟,如果您不知道如何处理 0 个参数,那么您可能也不知道如何处理 >= 2 个参数,反之亦然。我没有。
      【解决方案4】:

      在 C++17 中,您可以使用 std::map::try_emplace,它在内部使用 std::piecewise_construct,并且看起来不那么麻烦。它还接受一个键作为第一个参数(而不是像emplace 那样将所有内容都转发到std::pair::pair())。

      #include <map>
      
      struct A {
          A() = default;
      };
      
      int main()
      {
          std::map<int, A> map;
      
          map.emplace(std::piecewise_construct,
                      std::forward_as_tuple(10),
                      std::forward_as_tuple());
          // ...vs...
          map.try_emplace(10);
      }
      

      Live example.

      【讨论】:

      • 是的,try_emplace() 几乎就是 emplace() 应该的样子。
      【解决方案5】:
      Class ToolMap() 
      {
       friend class std::map;
        public (std::map)Object ToolMap()
        {
           return Object;
        }
      
      }
      

      【讨论】:

      • 这在很长一段时间内都不是有效的 C++,即使是,也没有机会回答这个问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-25
      • 1970-01-01
      相关资源
      最近更新 更多