如何在c++中迭代集合
How do I iterate over collections generically in C++?
简单地说,如果我有一个集合和一个向量,我如何创建一个通用的方法来处理这两个参数呢?
我要做的就是遍历这两种类型的集合。听起来应该是微不足道的,但我遗漏了一些东西。
void printMeSomeStrings(somebaseclass<string> strings) {
for (auto& str : strings) {
cout << str << endl;
}
}
在c#中,我会传递IEnumerable或类似的东西。然后我可以遍历这个集合。任何解释答案的一般性阅读都将受到赞赏。
您可以使用模板。例如:
#include <iostream>
template<typename C>
void foo(C const& c)
{
std::cout << "{ ";
for (auto const& x : c)
{
std::cout << x << " ";
}
std::cout << "}";
}
你可以这样使用它:
#include <set>
#include <vector>
int main()
{
std::vector<int> v = {1, 2, 3};
foo(v);
std::cout << std::endl;
std::set<std::string> s = {"Hello,", "Generic", "World!"};
foo(s);
}
生活例子。
这就是设计迭代器的目的。
template <class It>
void print_some_strings(It first, It last) {
while (first != last)
std::cout << *first++ << 'n';
}
第一个选项是将执行迭代的代码放在模板中。这需要将实现公开给所有使用它的人,这有缺点。
基本上,将类型C
作为template
参数,然后根据该类型C
编写代码。
template<typename C>
void printMeSomeStrings(C&& strings) {
for (auto const& str : strings) {
cout << str << endl;
}
}
如果你想在接口和实现之间有一个强大的屏障,c++ 11的方法是在for
-iterable容器上进行类型擦除,然后公开for
-iterable容器,就像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);
}
这里的情况是,对于用于构造适配器的每种类型,我们为每种类型构建一个自定义实现。然后存储一个指向该自定义类的抽象基类的指针,并为每次对它的调用进行重定向。
这意味着任何专门化std::begin
或定义其自己的开始的东西都不需要关联:我们在使用点创建临时关系。
实例:http://ideone.com/xOqBkI
在c#中,我会传递IEnumerable或类似的东西。
c++使用更python化的duck类型方法来定义接口(在c++中通常称为概念),而不是使用继承。要在c++中进行duck输入,可以使用如下的模板函数:
template<typename C>
void printMeSomeStrings(const C& strings)
{
for (const auto& str : strings)
{
cout << str << endl;
}
}
在python中,duck类型是在运行时完成的,但在c++中,它是在编译时完成的,所以duck类型没有运行时成本,并且在编译时也检查了所有内容。
这里是关于c++的更多信息,以帮助查找信息。首先,等价于IEnumerator<T>
的是c++中的迭代器。这里有一页介绍了不同的迭代器类别,以及需要为迭代器实现什么。由于遗留原因,迭代器是按照C中的指针建模的,这使得您可以将C数组与标准c++算法一起使用。
但是,与IEnumerator<T>
不同,迭代器必须成对出现。开始和结束的迭代器(在最后一个元素的后面)。因此,c++中IEnumerable<T>
的等价物称为range。在c++ 11中,范围由两个自由函数begin(T)
和end(T)
定义(也可以作为成员函数.begin()
和.end()
实现)。
通过将概念(又名接口)定义为两个自由函数,而不是使用继承,可以非侵入性地实现范围。例如,如果你有一些使用C风格链表的旧api。
- 带过滤器的现代迭代c++集合
- 集合上的输出迭代器:assign和increment迭代器
- 实现一个在集合上迭代的模板函数
- C++集合的嵌套迭代器
- 基于函数而不是集合的二分搜索或迭代器?
- 成员函数不能为集合迭代器和const_iterator的输入重载(但可以为其他 STL 迭代器重载)
- 如何在集合中迭代替代元素(或进行特定大小的飞跃)?
- C 模板功能在任何集合成员字段上迭代
- 如何在C++中迭代集合映射(std::map<std::set< char>, int >)?
- 通过C 中的迭代器中的集合中的集合
- Qt:为模板(映射、列表、集合等)构建一个可变迭代器
- C 迭代器到集合的最后一个元素
- 为什么迭代器不返回集合的开头
- 是同一集合上的两个不同的迭代器
- 在C 中初始化集合迭代器
- C++ 集合有序迭代 - 是迭代集合的结果,按标准排序
- 我在迭代集合时遇到问题
- 迭代集合并集的干净方法
- 如何在c++中迭代集合
- 如何在C++中迭代集合(std::map<string,std::set< string> >)的映射?