Java允许类显示Iterable
类型,以便客户端可以遍历某个实例的数据集合,如下所示:
public class MyClass
{
private ArrayList<String> strings;
private ArrayList<Integers> ints;
public MyClass() { /* generate data ... */ }
public Iterable<String> allStrings() {return strings;}
public Iterable<Integer> allInts() {return ints;}
}
这总是让我感到“干净”,因为它保持了封装,允许我根据需要将ArrayList
更改为LinkedList
,并且在构造中仍然方便客户端,例如for(String s : myClassInstance.allStrings()) //...
。
但是,在C ++中,如果我想允许客户端使用我的for-loop
,在没有Iterable
的情况下,我需要返回const vector<T>&
或其他什么,这显然是不太好。
定义template<> begin<my_class> {/*...*/}
和朋友很好,但仅当my_class
有一个集合要迭代时。我还能做什么?
答案 0 :(得分:21)
在C ++中创建Iterable
template<class T, class U>
struct Iterable
{
T _begin;
U _end;
Iterable(T begin, U end)
: _begin(begin), _end(end)
{}
T begin()
{
return _begin;
}
U end()
{
return _end;
}
};
template<class T, class U>
Iterable<T,U> make_iterable(T t, U u)
{
return Iterable<T,U>(t, u);
}
struct MyClass
{
std::vector<int> _ints;
std::vector<std::string> _strings;
auto allInts() -> decltype(make_iterable(_ints.begin(), _ints.end()))
{
return make_iterable(_ints.begin(), _ints.end());
}
auto allStrings() -> decltype(make_iterable(_strings.begin(), _strings.end()))
{
return make_iterable(_strings.begin(), _strings.end());
}
};
然后像
一样使用它for (auto i : foo.allInts())
{
cout << i << endl;
}
for (auto i : foo.allStrings())
{
cout << i << endl;
}
答案 1 :(得分:10)
告诉,不要问
如果您经常通过允许调用者遍历元素来暴露类的内部表示,那么封装在哪里?集合类需要公开它们的元素 - 这就是它们的目的。
在C ++中,提供visitor pattern或其他double-dispatch机制会更好,更高效,这样内部集合中的每个元素都可以按顺序传递给Functor。通过这种方式,包含/可访问类可以保证集合在迭代时不会改变,如果通过迭代器分发集合,这可能是不可能的。
示例:
#include <algorithm>
template<typename T>
struct ElementPrinter
{
void operator()(const T& elem) {
std::cout << elem << std::endl;
}
};
class MyClass
{
public:
// (1) if you only want to print the elements
std::ostream& print(std::ostream& s) const {
for (std::vector<int>::const_iterator it = myInts.begin(),
end=myInts.end(); it != end; ++it) {
s << *it << endl;
}
return s;
}
// (2) if you want a generic way to visit the ints
template<typename Functor>
void visitInts(Functor f) {
std::for_each(myInts.begin(), myInts.end(), f);
}
};
MyClass m;
// (1)
m.print(std::cout);
// (2)
m.visitInts(ElementPrinter());
答案 2 :(得分:0)
我有一个可能的解决方案(虽然我希望还有其他解决方案)是放弃漂亮的增强型for循环并定义几个begin/end
成员函数,即my_class::begin_strings(), my_class::end_strings(), my_class::end_ints(), my_class::begin_ints()
。
希望还有其他选择,因为现在客户必须使用for(auto it = mci.begin_strings(); it != mci.end_strings(); ++it)
。还不错,但仍然没有Java那么漂亮(以及大量的代码冗余)。