【问题标题】:using remove_if for a map container将 remove_if 用于地图容器
【发布时间】:2015-05-14 06:34:37
【问题描述】:

我试图将 remove_if 模板用于地图容器,但我收到模板参数的编译器错误。我无法理解为什么。

int main()
{
  map<const int, int> intmap;

  intmap[1] = 1;
  intmap[2] = 2;
  intmap[3] = 3;
  intmap[4] = 4;

  auto isOdd = [&](pair<const int, int> it)->bool 
     { return static_cast<bool>(it.second % 2); };

  isOdd(*(intmap.begin()));

 remove_if(intmap.begin(), intmap.end(), isOdd); 
}

此 remove_if 引发编译器错误。有什么建议可以解决吗?

错误信息是

C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\utility(260) : error C2166: l-value specifies const object
        C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\utility(259) : while compiling class template member function 
        'std::pair<_Ty1,_Ty2> &std::pair<_Ty1,_Ty2>::operator =(std::pair<_Ty1,_Ty2> &&)'
        with
        [
            _Ty1=const int,
            _Ty2=int
        ]
        maperaseif.cpp(29) : see reference to class template instantiation 'std::pair<_Ty1,_Ty2>' being compiled
        with
        [
            _Ty1=const int,
            _Ty2=int
        ]

【问题讨论】:

  • 请清楚说明您遇到的错误。
  • remove_if 最终会重新排序范围内的元素。 map 强制执行特定命令。因此 2 不兼容。细节只是在弄乱类型和const

标签: c++ stl remove-if


【解决方案1】:

你不能在map上使用remove_if,因为它的值类型实际上是std::pair&lt;const Key, Value&gt;,但是你看requirements of remove_if,你可以看到,解引用的迭代器类型应该是MoveAssignable

只写循环,或者使用 boost。

【讨论】:

  • 我看不到该语句的两半之间的联系。请注意,他的 lamdba 需要 std::pair&lt;const int, int&gt;
  • @BoBTFish 查看 std::remove_if 的要求。
  • 我并不是要暗示你的原始答案是错误的,只是它并没有真正解释自己。
【解决方案2】:

remove_if 通过扫描元素来工作,一旦要删除一个元素,它会记住将离开的“间隙”(保持一个迭代器指向它),同时推进另一个迭代器以找到下一个要保留的元素......然后它开始将元素从后者位置复制或移动到前者,直到到达end()

这不适用于map,因为您不能完全覆盖pair&lt;key,value&gt; 元素:不允许修改键值,否则实现需要的排序顺序不变式可能会失效。

因此,您需要放弃remove_if。您可以使用普通循环,小心保存迭代器到下一个元素,而不是尝试从刚刚擦除的迭代器前进。关于如何在迭代时从地图中删除元素的许多其他问题,例如here....

【讨论】:

    【解决方案3】:

    这个小erase_if 模板函数应该可以满足您的需求。 (不是我写的,只是从某个地方捡到的——所以感谢写的人!)

      template< typename ContainerT, typename PredicateT >
      void erase_if( ContainerT& items, const PredicateT& predicate ) {
        for( auto it = items.begin(); it != items.end(); ) {
          if( predicate(*it) ) it = items.erase(it);
          else ++it;
        }
      };
    

    在你的例子中,你会这样使用它:

    erase_if(intmap, isOdd); 
    

    【讨论】:

    • 我什至尝试过命名空间 generic { template void erase_if(ContainerT& items, const PredicateT& predicate) { for(auto it = items.begin(); it != items. end(); ) { if ( 谓词(*it)) it = items.erase(it);否则++它; } }; } generic::erase_if, [&](pair it){} > (intmap, isOdd);但这也无法编译抱怨 PredicateT 应该是一种类型。
    • @AwanishGolwara generic::erase_if&lt;map&lt;const int, int&gt;, [&amp;](pair&lt;const int, int&gt; it){} &gt; (intmap, isOdd); 错误。对于免费功能,您无需指定类型。仅供参考,如果您需要知道 lambda 的类型,您需要 decltype(isOdd)。所以,如果要指定类型,应该是erase_if&lt;std::map&lt;const int, int&gt;, decltype(isOdd)&gt;(intmap, isOdd);
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-17
    • 1970-01-01
    • 2011-04-19
    • 2013-06-29
    • 2010-10-22
    相关资源
    最近更新 更多