假设我有一个包含大量不同数据的链表。
class Node
{
public:
Node* next;
AAA dataA;
BBB dataB;
CCC dataC;
};
有没有办法让一个迭代器迭代我指定的任何变量(而不是为每个变量分别设置三个)。我知道迭代器可以使用模板使它迭代AAA,BBB或CCC类型,但我不知道如何指定返回哪个变量。
答案 0 :(得分:3)
我发现这样做的最好方法是使用boost bind和boost 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();
}
};