是否有一种通用的方法来迭代一组对象中的特定变量?

时间:2009-08-09 19:16:55

标签: c++ iterator

假设我有一个包含大量不同数据的链表。

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

有没有办法让一个迭代器迭代我指定的任何变量(而不是为每个变量分别设置三个)。我知道迭代器可以使用模板使它迭代AAA,BBB或CCC类型,但我不知道如何指定返回哪个变量。

7 个答案:

答案 0 :(得分:3)

我发现这样做的最好方法是使用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());

答案 1 :(得分:2)

一种可能的解决方案是将迭代器和访问分成不同的类:

Iterator类,它通过模板参数封装对数据的访问:

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
};

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

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方法中使用。

答案 2 :(得分:1)

根据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!
}

答案 3 :(得分:0)

无法在C ++中创建模板,该模板本身将迭代类型的成员。这样做需要在课程中以模板专业化,特殊方法等形式提供一些帮助......

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

不是说它不能完成(它可以),只是这比简单的模板方法更复杂。

答案 4 :(得分:0)

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

答案 5 :(得分:0)

你的问题实际上只是一个问题。

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

我猜你要对每个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 ) );
}

答案 6 :(得分:0)

如果我理解正确,您希望迭代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();
    }
};