如何使c++中的for each循环函数与自定义类一起工作
How to make the for each loop function in C++ work with a custom class
我是C/c++编程新手,但我已经用c#编程1.5年了。我喜欢c#,也喜欢List类,所以我考虑在c++中创建一个List类作为练习。
List<int> ls;
int whatever = 123;
ls.Add(1);
ls.Add(235445);
ls.Add(whatever);
实现类似于任何Array List类。我有一个T* vector
成员,我在那里存储项目,当这个存储即将被完全填满时,我调整它的大小。
请注意,这不是在生产中使用的,这只是一个练习。我很清楚vector<T>
和朋友。
现在我想循环遍历列表中的项。我不喜欢用for(int i=0;i<n; i==)
。我在visual studio中输入for
,等待智能感知,它建议我这样做:
for each (object var in collection_to_loop)
{
}
这显然不适用于我的List实现。我想我可以做一些宏观魔术,但这感觉像是一个巨大的黑客。实际上,最困扰我的是像这样传递类型:
#define foreach(type, var, list)
int _i_ = 0;
##type var;
for (_i_ = 0, var=list[_i_]; _i_<list.Length();_i_++,var=list[_i_])
foreach(int,i,ls){
doWork(i);
}
我的问题是:有没有办法使这个自定义列表类工作与foreach-like
循环?
首先,C++
中for-each
循环的语法与C#
不同(它也称为range based for loop
)。它的形式是:
for(<type> <name> : <collection>) { ... }
例如,对于std::vector<int> vec
,它应该是这样的:
for(int i : vec) { ... }
实际上有效地使用了返回迭代器的begin()
和end()
成员函数。因此,为了允许您的定制类利用for-each
循环,您需要提供一个begin()
和一个end()
函数。它们通常是重载的,返回iterator
或const_iterator
。实现迭代器可能很棘手,尽管对于类向量来说并不太难。
template <typename T>
struct List
{
T* store;
std::size_t size;
typedef T* iterator;
typedef const T* const_iterator;
....
iterator begin() { return &store[0]; }
const_iterator begin() const { return &store[0]; }
iterator end() { return &store[size]; }
const_iterator end() const { return &store[size]; }
...
};
实现了这些,就可以像上面那样使用基于范围的循环。
设iterable
的类型为Iterable
。然后,为了使
for (Type x : iterable)
编译时,必须有Type
和IType
类型,并且必须有函数
IType Iterable::begin()
IType Iterable::end()
IType
必须提供
Type operator*()
void operator++()
bool operator!=(IType)
整个结构是非常复杂的语法糖对于像
这样的东西for (IType it = iterable.begin(); it != iterable.end(); ++it) {
Type x = *it;
...
}
可以使用任何兼容类型(如const Type
或Type&
)代替Type
,这将具有预期的含义(constness, reference-instead- copy等)。
由于整个展开是在语法上进行的,因此您还可以稍微更改操作符的声明,例如,根据需要让*it返回引用或让!=取const IType& rhs
。
请注意,如果*it
不返回引用,则不能使用for (Type& x : iterable)
形式(但如果它返回引用,则也可以使用复制版本)。
还要注意operator++()
定义了++
操作符的前缀版本——但是它也将被用作后缀操作符,除非您显式地定义了后缀++
。如果只提供后缀++
,则range -for将无法编译。可以声明为operator++(int)
(假int参数)。
最小工作示例:
#include <stdio.h>
typedef int Type;
struct IType {
Type* p;
IType(Type* p) : p(p) {}
bool operator!=(IType rhs) {return p != rhs.p;}
Type& operator*() {return *p;}
void operator++() {++p;}
};
const int SIZE = 10;
struct Iterable {
Type data[SIZE];
IType begin() {return IType(data); }
IType end() {return IType(data + SIZE);}
};
Iterable iterable;
int main() {
int i = 0;
for (Type& x : iterable) {
x = i++;
}
for (Type x : iterable) {
printf("%d", x);
}
}
输出0123456789
你可以使用下面的宏来设置range -for-each(例如对于旧的c++编译器):
#define ln(l, x) x##l // creates unique labels
#define l(x,y) ln(x,y)
#define for_each(T,x,iterable) for (bool _run = true;_run;_run = false) for (auto it = iterable.begin(); it != iterable.end(); ++it)
if (1) {
_run = true; goto l(__LINE__,body); l(__LINE__,cont): _run = true; continue; l(__LINE__,finish): break;
} else
while (1)
if (1) {
if (!_run) goto l(__LINE__,cont);/* we reach here if the block terminated normally/via continue */
goto l(__LINE__,finish);/* we reach here if the block terminated by break */
}
else
l(__LINE__,body): for (T x = *it;_run;_run=false) /* block following the expanded macro */
int main() {
int i = 0;
for_each(Type&, x, iterable) {
i++;
if (i > 5) break;
x = i;
}
for_each(Type, x, iterable) {
printf("%d", x);
}
while (1);
}
(如果你的编译器甚至没有auto,请使用declspec或pass type)。
输出: 1234500000
可以看到,continue
和break
由于其复杂的结构可以使用它。请参阅http://www.chiark.greenend.org.uk/~sgtatham/mp/了解更多c预处理器编程,以创建自定义控制结构。
智能感知建议的语法不是c++;或者是MSVC的扩展。
c++ 11有基于范围的for循环,用于遍历容器的元素。您需要为您的类实现begin()
和end()
成员函数,它们将分别返回到第一个元素和最后一个元素的迭代器。当然,这意味着您还需要为您的类实现合适的迭代器。如果你真的想走这条路,你可以看看boost。iteratorfacade;它大大减少了自己实现迭代器的痛苦。
之后你可以这样写:
for( auto const& l : ls ) {
// do something with l
}
另外,由于您是c++新手,我想确保您知道标准库有几个容器类。
c++在语法中没有for_each
循环特性。必须使用c++11或使用模板函数std::for_each.
#include <vector>
#include <algorithm>
#include <iostream>
struct Sum {
Sum() { sum = 0; }
void operator()(int n) { sum += n; }
int sum;
};
int main()
{
std::vector<int> nums{3, 4, 2, 9, 15, 267};
std::cout << "before: ";
for (auto n : nums) {
std::cout << n << " ";
}
std::cout << 'n';
std::for_each(nums.begin(), nums.end(), [](int &n){ n++; });
Sum s = std::for_each(nums.begin(), nums.end(), Sum());
std::cout << "after: ";
for (auto n : nums) {
std::cout << n << " ";
}
std::cout << 'n';
std::cout << "sum: " << s.sum << 'n';
}
正如@yngum所建议的那样,您可以通过在集合上定义begin()
和end()
方法来返回自定义迭代器,从而使vc++ for each
扩展与任何任意集合类型一起工作。迭代器反过来必须实现必要的接口(解引用操作符、自增操作符等)。我这样做是为了包装遗留代码的所有MFC集合类。这是一点工作,但可以完成
- 将 exprtk 与自定义类的对象一起使用
- 如何将自定义记录器与websocketpp一起使用?
- 如何将来自 Boost.Python 的map_indexing_suite与自定义而不是标准对象一起使用?
- 是否可以将 std::basic_ifstream 和 std::basic_ofstream 与自定义缓冲区一起使用?
- gtest 期望无序元素与自定义比较器/匹配器一起使用
- 如何将unordered_set与自定义结构一起使用?
- 如何在 C++11 中将 std::max 与自定义比较器一起使用?
- 如何将 std::num_put 与自定义迭代器一起使用?
- 将 OpenGL VBO 与自定义类/数据结构一起使用
- 与自定义命名空间一起使用时实现 std::error_category、名称解析问题
- 如何将重新绑定与自定义分配器和自定义列表一起使用
- 设置要与无序集合一起使用的自定义类 - 在集合中找不到元素
- 将 boost odeint 与 std::map 和自定义向量空间代数一起使用时,没有可行的重载'='
- shared_ptr自定义分配器与自定义删除器一起
- 创建自定义迭代器结构以与CSTDIO一起使用
- 将CloudViewer与自定义Pointt类型一起使用
- 将 std::make_unique 与自定义删除器一起使用
- 将 std::find 与自定义 Book 类一起使用
- 将运算符new和运算符delete与自定义内存池/分配器一起使用
- 将 at() 与自定义对象的向量一起使用时的分割错误