第一种选择是将迭代代码放入模板中。这需要向使用它的所有人公开实现,具有不利之处。
基本上,将类型
C
作为
template
参数,然后按照该类型
C
编写代码。
template<typename C>
void printMeSomeStrings(C&& strings) {
for (auto const& str : strings) {
cout << str << endl;
}
}
如果你想在接口和实现之间建立强大的屏障,C++11的方法是对可迭代容器进行类型擦除,然后公开一个类似于
std::function
的可迭代容器。
这有些棘手。我个人认为编写
for_each
函数比编写完整的迭代适配器更容易。如果你想要完整的容器迭代类型擦除对象,请从
boost
开始,或者在下面向我提问,我可能会做。
不过,
for_each
适配器很容易。
#include <functional>
#include <utility>
#include <iterator>
#include <memory>
template<typename T>
struct for_each_helper_interface {
virtual ~for_each_helper_interface() {}
virtual void for_each( std::function< void(T) > const& ) = 0;
};
template<typename C, typename T>
struct for_each_helper:for_each_helper_interface<T> {
C& c;
for_each_helper( C& in ):c(in) {}
virtual void for_each( std::function< void(T) > const& f ) override final {
for( auto&& x:c ) {
f(x);
}
}
};
template<typename T>
struct for_each_adaptor {
std::unique_ptr<for_each_helper_interface<T>> pImpl;
void for_each( std::function< void(T) > const& f ) {
if (pImpl) {
pImpl->for_each(f);
}
}
template<typename C>
for_each_adaptor( C&& c ): pImpl( new for_each_helper<C, T>( std::forward<C>(c) ) ) {}
};
这将对T
的容器(或可转换为T
的类型)进行类型擦除,并公开一个for_each
方法,让您可以迭代容器的内容。使用方法如下:
#include <set>
#include <iostream>
#include <vector>
void print_stufF( for_each_adaptor<std::string const&> c ) {
c.for_each([&](std::string const&s){
std::cout << s << "\n";
});
}
int main() {
std::set<std::string> s;
s.insert("hello");
s.insert("world");
print_stuff(s);
std::vector<std::string> v;
v.push_back("hola");
v.push_back("bola");
print_stuff(v);
}
这里发生的情况是,对于用于构建我们的适配器的每种类型,我们都会构建一个自定义的 for each 实现。然后,我们存储指向此自定义类的抽象基类的指针,并将 for each 调用重定向到它。
这意味着任何专门化 std::begin 或定义其自己的 begin 的内容都不需要相关:我们在使用时创建临时关系。
实时示例:
http://ideone.com/xOqBkI