【问题标题】:How should std::map be used with a value that does not have a default constructor?std::map 应该如何与没有默认构造函数的值一起使用?
【发布时间】:2012-01-10 08:45:38
【问题描述】:

我有一个想要放入地图的值类型。 它有一个不错的默认复制构造函数,但没有默认构造函数。

我相信只要不使用operator[],一切都会好起来的。

但是,我最终得到了像这样非常丑陋的结构来实际插入一个对象。 (我认为如果该键已经存在值,则插入会失败)。

// equivalent to m[5]=x but without default construction

std::map<int,X>::iterator it = m.find(5);
if( it != m.end() )
{
   m->second = x;
}
else
{
  m->insert( std::make_pair(5,x) );
}

我相信它会扫描地图两次,而且看起来也很丑。

有没有更简洁/更有效的方法来做到这一点?

【问题讨论】:

  • @miaout17,是的,现有答案将解决它。然而,解决的问题是 IMO 的另一个问题。
  • @miaout17,实际上这并不能完全解决它。在这种情况下,如果一个新值已经存在,您就不会将新值存储到地图中。但不要用新值覆盖它。需要稍微调整功能才能做到这一点......有人发布了一个正确执行此操作的解决方案,然后删除了他们的答案:(

标签: c++


【解决方案1】:

您可以使用标准的insert 函数简单地“插入或覆盖”:

auto p = mymap.insert(std::make_pair(key, new_value));

if (!p.second) p.first->second = new_value;  // overwrite value if key already exists

如果您想通过引用传递元素,请明确表示该对:

insert(std::pair<K&, V&>(key, value));

如果您有类似map_t 的地图类型定义,您可以说std::pair&lt;map_t::key_type &amp;, map_t::mapped_type &amp;&gt;,或此主题的任何合适的变体。


也许这最好包裹在一个助手中:

template <typename Map>
void insert_forcefully(Map & m,
                       typename Map::key_type const & key,
                       typename Map::mapped_type const & value)
{
  std::pair<typename Map::iterator, bool> p = m.insert(std::pair<typename Map::key_type const &, typename Map::mapped_type const &>(key, value));
  if (!p.second) { p.first->second = value; }
}

【讨论】:

  • 我唯一遇到的问题是从keynew_value 构造一对可能很昂贵,或者在C++11 的情况下是不可能的(new_value 可能是 move-只要)。我在我的代码中遇到了这个问题。 :(
  • @GMan:为什么不使用一对引用?实际上,make_pair 是否推断出引用类型?否则使用std::tie
  • 这两个都行不通,因为insert 需要pair_type。这真的只是容器的一个缺点。
  • @GMan:是的,没错,tie 生成元组,而不是 pairs。嗯,你可以明确引用对;我添加了那个。
  • 它不会仍然复制吗,因为insert() 本身引用了map::value_type,这是一个pair&lt;const K, V&gt;,而不是一对引用?
【解决方案2】:

您可以先获取插入 lower_bound 对的位置,然后检查它是否已经存在,如果不存在,则插入它,并提供迭代器插入的位置。类似的东西。

【讨论】:

    【解决方案3】:

    map(之类的)的界面你漏掉了两件事:

    • insert(value_type) 返回一个 std::pair&lt;iterator, bool&gt;.first 成员指向带有您尝试插入的键的元素,.second 成员指示它实际上是您尝试插入的元素还是之前在容器中的另一个元素.
    • insert(iterator, value_type) 允许您提示插入位置

    不过,后者在您的情况下不一定有用。

    typedef std::map<int,X> Map;
    
    // insert and check
    std::pair<Map::iterator, bool> const result =
      map.insert(std::make_pair(5, x));            // O(log N)
    
    if (not result.second)
    {
      result->first.second = x;                    // O(1)
      // OR
      using std::swap;
      swap(result->first.second, x);
    }
    

    如果你的 type 不支持赋值并且没有swap,但是,你需要硬着头皮:

    // locate and insert
    Map::iterator position = map.lower_bound(5);         // O(log N)
    if (position != map.end() and position->first == 5) 
    {
      position = map.erase(position);                    // O(1)
    }
    map.insert(position, std::make_pair(5, x));          // O(log N) if rebalancing
    

    在 C++11 中,insert 方法加倍:

    • insert(value_type const&amp;) // 复制插入
    • insert(P&amp;&amp;) // 移动插入

    通过完美转发,我们得到了新的emplace 方法。类似于 insert,但它通过将参数转发给其构造函数来构造元素。不过,它如何区分 key 和 value 的参数对我来说是个谜。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-31
      • 1970-01-01
      • 2020-05-19
      • 2010-12-11
      • 1970-01-01
      • 1970-01-01
      • 2011-05-06
      • 1970-01-01
      相关资源
      最近更新 更多