【问题标题】:Use of for_each on map elements在地图元素上使用 for_each
【发布时间】:2011-02-20 11:23:19
【问题描述】:

我有一张地图,我想在其中调用每个数据类型对象的成员函数。我还知道如何在任何序列上执行此操作,但是是否可以在关联容器上执行此操作?

我能找到的最接近的答案是:Boost.Bind to access std::map elements in std::for_each。但是我不能在我的项目中使用 boost 那么,是否有我缺少的 STL 替代方案来 boost::bind?

如果不可能,我想为指向数据对象的指针创建一个临时序列,然后在其上调用 for_each,如下所示:

class MyClass
{
public:
 void Method() const;
}

std::map<int, MyClass> Map;
//...

std::vector<MyClass*> Vector;
std::transform(Map.begin(), Map.end(), std::back_inserter(Vector), std::mem_fun_ref(&std::map<int, MyClass>::value_type::second));
std::for_each(Vector.begin(), Vector.end(), std::mem_fun(&MyClass::Method));

它看起来太模糊了,我不太喜欢它。有什么建议吗?

【问题讨论】:

    标签: c++ algorithm stl stdmap std-pair


    【解决方案1】:

    C++11 允许你这样做:

    for (const auto& kv : myMap) {
        std::cout << kv.first << " has value " << kv.second << std::endl;
    }
    

    C++17 允许你这样做:

    for (const auto& [key, value] : myMap) {
        std::cout << key << " has value " << value << std::endl;
    }
    

    使用structured binding

    更新:

    如果您不想修改地图,则 const auto 会更安全。

    【讨论】:

    • 我喜欢 C++ 17 选项,但遗憾的是,如果您在 for 正文中使用 lambda,则无法捕获 :(
    【解决方案2】:

    您可以遍历 std::map 对象。每个迭代器将指向一个std::pair&lt;const T,S&gt;,其中TS 与您在map 中指定的类型相同。

    这里是:

    for (std::map<int, MyClass>::iterator it = Map.begin(); it != Map.end(); ++it)
    {
      it->second.Method();
    }
    

    如果您仍想使用std::for_each,请传递一个以std::pair&lt;const int, MyClass&gt;&amp; 作为参数的函数。

    例子:

    void CallMyMethod(std::pair<const int, MyClass>& pair) // could be a class static method as well
    {
      pair.second.Method();
    }
    

    并将其传递给std::for_each:

    std::for_each(Map.begin(), Map.end(), CallMyMethod);
    

    【讨论】:

    • 感谢您的回答,但我想避免创建自己的循环。
    • @Antonio:有什么特别的原因吗?任何基于模板的解决方案都应该生成一个最多同样高效或更差的代码。但不太可能更快。我编辑了我的答案以添加一个更短(但等效的解决方案)。
    • 我只是发现在 STL 序列上为此目的使用 for_each 比 for 循环 (for_each(v.begin(), v.end(), &mem_fun_ref(&MyClass::Method) )。所以我在地图上徘徊了如何做到这一点。
    • @Antonio:问题已经过编辑,因此您也可以使用std::for_each。试一试;)
    • 由于一个小错误而无法编译。取消引用时,std::map 迭代器返回 std::pair&lt;const key_type, value_type&gt;,而不是 std::pair&lt;key_type, value_type&gt;。所以你需要CallMyMethodstd::pair&lt;const int, MyClass&gt;&amp; 作为它的参数。
    【解决方案3】:

    C++14 带来了通用 lambda。 这意味着我们可以非常轻松地使用 std::for_each:

    std::map<int, int> myMap{{1, 2}, {3, 4}, {5, 6}, {7, 8}};
    
    std::for_each(myMap.begin(), myMap.end(), [](const auto &myMapPair) {
        std::cout << "first " << myMapPair.first << " second "
                  << myMapPair.second << std::endl;
    });
    

    我认为 std::for_each 有时比基于简单范围的 for 循环更适合。例如,当您只想遍历地图的子集时。

    【讨论】:

    • 明确的答案,因为这种方法在 4 年内得到了支持。
    【解决方案4】:

    普通的 C++ 怎么样? (根据@Noah Roberts 的注释修复的示例)

    for(std::map<int, MyClass>::iterator itr = Map.begin(), itr_end = Map.end(); itr != itr_end; ++itr) {
      itr->second.Method();
    }
    

    【讨论】:

    • 算法效率更高
    • 过早的概括是另一种罪恶的根源。
    • 可以说,对于下一个程序员来说,它们更难理解,并且可维护的代码比优化的代码更有价值,除非您使用分析工具确定了瓶颈。
    • @jmucchiello - 这当然取决于口味,但我希望与您有所不同。事实上恰恰相反,因为算法是以循环应该做的事情命名的,而不是必须通过循环中发生的步骤来破译它。
    • ...可能可以忽略不计。)也就是说,我不喜欢for_each。大多数时候它很笨拙,而且表达能力也不是很强。理想情况下,我更喜欢 C++0x 中基于范围的新 for 循环:for (const map_pair&amp; p : the_map) { the_vec.push_back(p.second); }for (vec_element v&amp; : the_vec) { v.method(); }。阅读起来要简单得多,而且非常简洁。接下来是伪造这样的功能,例如为每个宏使用 Boost。如果不是这样,lambda 可以通过本地化算法提供一点帮助,但我认为for_each 是最后的手段。
    【解决方案5】:

    不幸的是,您没有 Boost,但是如果您的 STL 实现具有扩展,那么您可以组合 mem_fun_ref 和 select2nd 来创建适合与 for_each 一起使用的单个仿函数。代码看起来像这样:

    #include <algorithm>
    #include <map>
    #include <ext/functional>   // GNU-specific extension for functor classes missing from standard STL
    
    using namespace __gnu_cxx;  // for compose1 and select2nd
    
    class MyClass
    {
    public:
        void Method() const;
    };
    
    std::map<int, MyClass> Map;
    
    int main(void)
    {
        std::for_each(Map.begin(), Map.end(), compose1(std::mem_fun_ref(&MyClass::Method), select2nd<std::map<int, MyClass>::value_type>()));
    }
    

    请注意,如果您无权访问 compose1(或 unary_compose 模板)和 select2nd,则它们相当容易编写。

    【讨论】:

    • 如果您是 VS 用户并且您知道如何访问 STL 扩展,请编辑此以实现跨平台兼容性。
    • “因为 libstdc++ 库的 STL 子部分的实现基于 SGI 3.3 实现,所以我们 [GNU] 也继承了它们的扩展。”他们做什么,有人可以移植他们。 gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.2/…
    • 注意,有人在这里尝试过(未经测试),虽然它没有很好的格式化或测试,但它几乎可以为我编译。似乎在 类中弄乱了 std::pair 并出现了一个奇怪的错误。 YMMV。 computer-programming-forum.com/84-vc-stl/4d17c0a678f187f0.htm
    【解决方案6】:

    对于从 google 偶然发现这个问题的程序员,有一个使用 boost 的好方法。

    在这里解释:Is it possible to use boost::foreach with std::map?

    为您提供方便的真实示例:

    // typedef in include, given here for info : 
    typedef std::map<std::string, std::string> Wt::WEnvironment::CookieMap
    
    Wt::WEnvironment::CookieMap cookie_map = environment.cookies();
    
    BOOST_FOREACH( const Wt::WEnvironment::CookieMap::value_type &cookie, cookie_map )
    {
        std::cout << "cookie : " << cookie.first << " = " << cookie.second << endl;
    }
    

    享受吧。

    【讨论】:

      【解决方案7】:

      它对你有用吗?

      class MyClass;
      typedef std::pair<int,MyClass> MyPair;
      class MyClass
      {
        private:
        void foo() const{};
      public:
      static void Method(MyPair const& p) 
      {
          //......
              p.second.foo();
      };
      }; 
      // ...
      std::map<int, MyClass> Map;
      //.....
      std::for_each(Map.begin(), Map.end(), (&MyClass::Method));
      

      【讨论】:

        【解决方案8】:

        只是一个例子:

        template <class key, class value>
        class insertIntoVec
        {
        public:
            insertIntoVec(std::vector<value>& vec_in):m_vec(vec_in)
            {}
        
            void operator () (const std::pair<key, value>& rhs)  
            {   
                m_vec.push_back(rhs.second);
            }
        
        private:
            std::vector<value>& m_vec;
        };
        
        int main()
        {
        std::map<int, std::string> aMap;
        aMap[1] = "test1";
        aMap[2] = "test2";
        aMap[3] = "test3";
        aMap[4] = "test4";
        
        std::vector<std::string> aVec;
        
        aVec.reserve(aMap.size());
        std::for_each(aMap.begin(), aMap.end(),
                  insertIntoVec<int, std::string>(aVec) 
            );
        

        }

        【讨论】:

        • 那不是最理想的。复制MyClass每个 实例并为此分配std::vector 并不完全是优化...
        • 上面的例子是为了展示如何使用std::for_each来迭代map。不存在优化问题
        • 我想我们可以假设 OP 在存在优化解决方案时会更喜欢它......不是吗?
        【解决方案9】:

        据我记得,C++ map 可以使用 map.begin() 返回一个键的迭代器,你可以使用该迭代器循环所有键,直到它到达 map.end(),并获得相应的值: C++ map

        【讨论】:

        • 不,它不能!迭代器总是给你一对 (key,value)
        【解决方案10】:

        我写这篇文章是为了满足您的需求。

        namespace STLHelpers
        {
            //
            // iterator helper type for iterating through the *values* of key/value collections
            //
        
            /////////////////////////////////////////////
            template<typename _traits>
            struct _value_iterator
            {
                explicit _value_iterator(typename _traits::iterator_type _it)
                    : it(_it)
                {
                }
        
                _value_iterator(const _value_iterator &_other)
                    : it(_other.it)
                {
                }
        
                friend bool operator==(const _value_iterator &lhs, const _value_iterator &rhs)
                {
                    return lhs.it == rhs.it;
                }
        
                friend bool operator!=(const _value_iterator &lhs, const _value_iterator &rhs)
                {
                    return !(lhs == rhs);
                }
        
                _value_iterator &operator++()
                {
                    ++it;
                    return *this;
                }
        
                _value_iterator operator++(int)
                {
                    _value_iterator t(*this);
                    ++*this;
                    return t;
                }
        
                typename _traits::value_type &operator->()
                {
                    return **this;
                }
        
                typename _traits::value_type &operator*()
                {
                    return it->second;
                }
        
                typename _traits::iterator_type it;
            };
        
            template<typename _tyMap>
            struct _map_iterator_traits
            {
                typedef typename _tyMap::iterator iterator_type;
                typedef typename _tyMap::mapped_type value_type;
            };
        
            template<typename _tyMap>
            struct _const_map_iterator_traits
            {
                typedef typename _tyMap::const_iterator iterator_type;
                typedef const typename _tyMap::mapped_type value_type;
            };
        }
        

        【讨论】:

          【解决方案11】:

          这是一个如何将 for_each 用于地图的示例。

          std::map<int, int> map;
          
          map.insert(std::pair<int, int>(1, 2));
          map.insert(std::pair<int, int>(2, 4));
          map.insert(std::pair<int, int>(3, 6));
          
          auto f = [](std::pair<int,int> it) {std::cout << it.first + it.second << std::endl; };
          std::for_each(map.begin(), map.end(), f);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-12-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-05-04
            • 2022-01-14
            • 2021-11-05
            • 1970-01-01
            相关资源
            最近更新 更多