【问题标题】:Is there a generic way to iterate over a specific variable in a group of objects?是否有一种通用的方法来迭代一组对象中的特定变量?
【发布时间】:2009-08-09 19:16:55
【问题描述】:

假设我有一个链接列表,其中包含一堆不同的数据。

class Node
{
public:
    Node* next;
    AAA dataA;
    BBB dataB;
    CCC dataC;
};

有没有一种方法可以创建一个迭代器来迭代我指定的任何变量(而不是为每个变量创建三个单独的)。我知道迭代器可以使用模板来迭代 AAA、BBB 或 CCC 类型,但我不知道如何指定要返回的变量。

【问题讨论】:

    标签: c++ iterator


    【解决方案1】:

    我发现最好的方法是使用 boost bindboost transform_iterator

    首先,您需要一个 Node 对象的集合和一个遍历该集合的迭代器。在我的示例中为简洁起见,我将使用 std::list。

    #include <boost/bind.hpp>
    #include <boost/iterator/transform_iterator.hpp>
    using boost;
    
    struct FunctionAAA
    {
        void operator() (const AAA& x)
        {}
    };
    
    struct FunctionBBB
    {
        void operator() (const BBB& x)
        {}
    };
    
    typedef std::list<Node> NodeList;
    NodeList collection;
    std::foreach (
        make_transform_iterator (collection->begin(), bind (&Node::dataA, _1)),
        make_transform_iterator (collection->end(), bind (&Node::dataA, _1)),
        FunctionAAA());
    
    std::foreach (
        make_transform_iterator (collection->begin(), bind (&Node::dataB, _1)),
        make_transform_iterator (collection->end(), bind (&Node::dataB, _1)),
        FunctionBBB());
    

    【讨论】:

      【解决方案2】:

      一种可能的解决方案是将迭代器和访问拆分为单独的类:

      通过模板参数封装对数据的访问的迭代器类:

      template <typename Access>
      class iterator
      {
      private:
        Node *current;
      
      public:
        iterator(Node *start)
          : current(start)
        {
        }
      
        typename Access::typeof &operator *() const
        {
          return Access::access(*current);
        }
      
        bool end() const
        {
          return (current == NULL);
        }
      
        iterator &operator++()
        {
          if (current !=  NULL)
          {
            current = current->Next;
          }
        }
      
        // ... other useful operators/methods
      };
      

      用于访问各种数据字段的类。 THose 可以用作迭代器类中的模板参数:

      class AccessDataA
      {
      public:
        typedef AAA typeof;
        static AAA &access(Node &node)
        {
          return node.dataA;
        }
      };
      
      class AccessDataB
      {
      public:
        typedef BBB typeof;
        static BBB &access(Node &node)
        {
          return node.dataB;
        }
      };
      
      class AccessDataC
      {
      public:
        typedef CCC typeof;
        static CCC &access(Node &node)
        {
          return node.dataC;
        }
      };
      

      示例用法:

      Node *start = ...;
      
      // Loop over B:
      for (iterator<AccessB> it(start); it++; !it.end())
      {
        // ... *it ...
      }
      
      // Loop over C:
      for (iterator<AccessC> it(start); it++; !it.end())
      {
        // ... *it ...
      }
      

      一项改进是添加与 STL 兼容的语义,以便您的列表和迭代器可以在 std::for_each 等 STL 方法中使用。

      【讨论】:

        【解决方案3】:

        根据 rstevens 的建议,我想我已经找到了一种几乎可以做我想做的事情的方法。我在类成员指针上查找了一些东西,并且能够通过这样做跳过中间人访问器类:

        template <typename T>
        class iterator
        {
        private:
            Node *current;
            T Node::*var;
        
        public:
            iterator()
                : current(NULL), var(NULL) {}
        
            iterator(Node *start, T Node::*var)
                : current(start), var(var)
            {
            }
        
            typename T &operator *() const
            {
                return current->*var;
            }
        
            bool end() const
            {
                return (current == NULL);
            }
        
            iterator &operator++()
            {
                if (current)
                    current = current->next;
                return *this;
            }
        };
        

        然后我修改了 Node 以提供方便的函数来制作迭代器:

        class Node
        {
        public:    
            Node* next;
            AAA dataA;
            BBB dataB;
            CCC dataC;
        
            typedef iterator<AAA> AIter;
            typedef iterator<BBB> BIter;
            typedef iterator<CCC> CIter;
        
            AIter getAIter()
            {
                return AIter(this, &Node::dataA);
            }
        
            BIter getBIter()
            {
                return BIter(this, &Node::dataB);
            }
        
            CIter getCIter()
            {
                return CIter(this, &Node::dataC);
            }
        };
        

        所以现在我可以这样做来轻松地遍历我的类的每个数据成员:

        for (Node::CIter iter = n1.getCIter(); !iter.end(); ++iter)
        {
            // tada!
        }
        

        【讨论】:

        • 我不喜欢这个解决方案,因为它对 Node 类具有侵入性。
        • 那么请不要在自己的代码中使用它。 :) 它解决了我 3 年前遇到的任何问题,这对我来说已经足够了。
        【解决方案4】:

        没有办法在 C++ 中创建一个模板,它本身会遍历一个类型的成员。这样做需要类中的一些帮助,包括模板特化、特殊方法等......

        模板本身相当复杂,需要大量的设置工作。原因是给定要迭代的特定类型,模板必须处理 N 个其他类型。即返回的成员的类型。

        不是说不能做(可以),只是这比简单的模板方法复杂多了。

        【讨论】:

          【解决方案5】:

          我怀疑您可以使用模板自动选择要返回的正确变量,除非指定三个模板特化,这与定义三个类相同。但是,您可以使用三种不同的方法创建一个迭代器类来分别返回 dataA、dataB 或 dataC(而不是 operator*())。

          【讨论】:

          • 我可以做一些棘手的工作模板并指定一个getter方法或变量吗?像 MyIterator 或 MyIterator 或类似的东西?
          【解决方案6】:

          您的问题实际上只是一部分问题。

          您可以创建一个迭代器适配器,它的行为就像它迭代AAA 的集合一样,但实际上是在迭代Nodes 的集合。不过,这可能不是解决潜在问题的最佳方法。

          我猜你想对每个aaa 成员执行某种操作。假设这是一个这样的函子。

          struct DoAAAAction
          {
              void operator()(AAA& a);
          };
          

          调整动作以作用于Node 可能更容易。

          template<class Action>
          class DataA_ActionAdapter
          {
          public:
              DataA_ActionAdapter( Action aa ) : a( aa ) {}
              void operator()(Node& n) { a(n.dataAAA); }
          private:
              Action a;
          };
          

          这允许您在 Node 迭代器上使用标准算法。

          template<class NodeIterator, class AAAAction>
          void TestAAA(NodeIterator first, NodeIterator last, AAAAction aaaa)
          {
              std::for_each( first, last, DataA_ActionAdapter<AAAAction>( aaaa ) );
          }
          

          【讨论】:

            【解决方案7】:

            如果我理解正确,您喜欢迭代 dataA、dataB 和 dataC - 这意味着 AAA、BBB 和 CCC 都共享相同的基本类型(或至少共享相似的特征)。为什么不简单地将它们存储在 std::vector 或 std::set 中?

            注意: AAA、BBB 和 CCC 都是从 NodeType 派生的

            class Node
            {
            public:
                Node()
                {
                    dataNodes.push_back(AAA());
                    dataNodes.push_back(BBB());
                    dataNodes.push_back(CCC());
                }
            
                // AAA dataA;
                // BBB dataB;
                // CCC dataC;
            
                std::vector < NodeType > dataNodes;
            
                std::vector < NodeType >::iterator begin()
                {
                    return dataNodes.begin();
                }
            
                std::vector < NodeType >::iterator end()
                {
                    return dataNodes.end();
                }
            };
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-11-21
              • 2014-05-24
              • 1970-01-01
              • 2020-03-25
              • 1970-01-01
              • 2020-10-24
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多