如何使 C++ 中的 for each 循环函数与自定义类一起使用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16504062/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make the for each loop function in C++ work with a custom class
提问by Ricardo Pieper
I'm new to C/C++ programming, but I've been programming in C# for 1.5 years now. I like C# and I like the List class, so I thought about making a List class in C++ as an exercise.
我是 C/C++ 编程的新手,但我已经用 C# 编程 1.5 年了。我喜欢 C#,也喜欢 List 类,所以我想在 C++ 中制作一个 List 类作为练习。
List<int> ls;
int whatever = 123;
ls.Add(1);
ls.Add(235445);
ls.Add(whatever);
The implementation is similar to any Array List class out there. I have a T* vector
member where I store the items, and when this storage is about to be completely filled, I resize it.
该实现类似于那里的任何 Array List 类。我有一个T* vector
成员存储项目,当这个存储空间即将被完全填满时,我调整了它的大小。
Please notice that this is not to be used in production, this is only an exercise. I'm well aware of vector<T>
and friends.
请注意,这不能用于生产,这只是一个练习。我vector<T>
和朋友都心知肚明。
Now I want to loop through the items of my list. I don't like to use for(int i=0;i<n; i==)
. I typed for
in the visual studio, awaited for Intellisense, and it suggested me this:
现在我想遍历我的列表中的项目。我不喜欢使用for(int i=0;i<n; i==)
. 我for
在视觉工作室中输入,等待 Intellisense,它向我建议:
for each (object var in collection_to_loop)
{
}
This obviously won't work with my List implementation. I figured I could do some macro magic, but this feels like a huge hack. Actually, what bothers me the most is passing the type like that:
这显然不适用于我的 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);
}
My question is: is there a way to make this custom List class work with a foreach-like
loop?
我的问题是:有没有办法让这个自定义 List 类与foreach-like
循环一起工作?
回答by Yuushi
Firstly, the syntax of a for-each
loop in C++
is different from C#
(it's also called a range based for loop
. It has the form:
首先,for-each
循环 in的语法C++
不同于C#
(它也称为 a range based for loop
。它具有以下形式:
for(<type> <name> : <collection>) { ... }
So for example, with an std::vector<int> vec
, it would be something like:
因此,例如,对于std::vector<int> vec
,它将类似于:
for(int i : vec) { ... }
Under the covers, this effectively uses the begin()
and end()
member functions, which return iterators. Hence, to allow your custom class to utilize a for-each
loop, you need to provide a begin()
and an end()
function. These are generally overloaded, returning either an iterator
or a const_iterator
. Implementing iterators can be tricky, although with a vector-like class it's not too hard.
在幕后,这有效地使用了begin()
和end()
成员函数,它们返回迭代器。因此,要允许您的自定义类使用for-each
循环,您需要提供一个begin()
和一个end()
函数。这些通常是重载的,返回 aniterator
或 a 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]; }
...
};
With these implemented, you can utilize a range based loop as above.
实现这些后,您可以使用上述基于范围的循环。
回答by masterxilo
Let iterable
be of type Iterable
.
Then, in order to make
让iterable
是类型Iterable
。然后,为了使
for (Type x : iterable)
compile, there must be types called Type
and IType
and there must be functions
编译,必须有被调用的类型Type
,IType
并且必须有函数
IType Iterable::begin()
IType Iterable::end()
IType
must provide the functions
IType
必须提供功能
Type operator*()
void operator++()
bool operator!=(IType)
The whole construction is really sophisticated syntactic sugar for something like
整个结构是非常复杂的语法糖,例如
for (IType it = iterable.begin(); it != iterable.end(); ++it) {
Type x = *it;
...
}
where instead of Type
, any compatible type (such as const Type
or Type&
) can be used, which will have the expected implications (constness, reference-instead-of-copy etc.).
其中代替Type
,可以使用任何兼容类型(例如const Type
或Type&
),这将具有预期的含义(一致性,引用代替复制等)。
Since the whole expansion happens syntactically, you can also change the declaration of the operators a bit, e.g. having *it return a reference or having != take a const IType& rhs
as needed.
由于整个扩展在语法上发生,您还可以稍微更改运算符的声明,例如让 *it 返回一个引用或让 != take aconst IType& rhs
根据需要。
Note that you cannot use the for (Type& x : iterable)
form if *it
does not return a reference (but if it returns a reference, you can also use the copy version).
请注意,for (Type& x : iterable)
如果*it
不返回引用,则不能使用表单(但如果返回引用,您也可以使用复制版本)。
Note also that operator++()
defines the prefixversion of the ++
operator -- however it will also be used as the postfix operator unless you explicitly define a postfix ++
. The ranged-for will not compile if you only supply a postfix ++
, which btw.can be declared as operator++(int)
(dummy int argument).
还要注意,operator++()
定义了运算符的前缀版本++
——但是,除非您明确定义后缀,否则它也将用作后缀运算符++
。如果您只提供一个 postfix ++
,则 ranged-for 将无法编译,顺便说一句,可以将其声明为operator++(int)
(dummy int 参数)。
Minimal working example:
最小工作示例:
#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);
}
}
output
输出
0123456789
You can fake the ranged-for-each (e.g. for older C++ compilers) with the following macro:
您可以使用以下宏伪造 ranged-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);
}
(use declspec or pass IType if your compiler doesn't even have auto).
(如果您的编译器甚至没有 auto,请使用 declspec 或传递 IType)。
Output:
输出:
1234500000
As you can see, continue
and break
will work with this thanks to its complicated construction.
See http://www.chiark.greenend.org.uk/~sgtatham/mp/for more C-preprocessor hacking to create custom control structures.
正如你所看到的,continue
并且break
将与此由于其复杂的建设工作。请参阅http://www.chiark.greenend.org.uk/~sgtatham/mp/了解更多 C 预处理器黑客攻击以创建自定义控制结构。
回答by Praetorian
That syntax Intellisense suggested is not C++; or it's some MSVC extension.
Intellisense 建议的语法不是 C++;或者它是一些 MSVC 扩展。
C++11 has range-based for loopsfor iterating over the elements of a container. You need to implement begin()
and end()
member functions for your class that will return iterators to the first element, and one past the last element respectively. That, of course, means you need to implement suitable iteratorsfor your class as well. If you really want to go this route, you may want to look at Boost.IteratorFacade; it reduces a lot of the pain of implementing iterators yourself.
C++11 具有基于范围的 for 循环,用于迭代容器的元素。您需要为您的类实现begin()
和end()
成员函数,它们将分别返回第一个元素的迭代器和最后一个元素的迭代器。当然,这意味着您还需要为您的类实现合适的迭代器。如果你真的想走这条路,你可能想看看Boost.IteratorFacade;它减少了自己实现迭代器的很多痛苦。
After that you'll be able to write this:
之后你就可以这样写了:
for( auto const& l : ls ) {
// do something with l
}
Also, since you're new to C++, I want to make sure that you know the standard library has several containerclasses.
另外,由于您是 C++ 新手,我想确保您知道标准库有几个容器类。
回答by saeed
C++ does not have the for_each
loop feature in its syntax. You have to use c++11 or use the template function std::for_each.
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';
}
回答by Scott Jones
As @yngum suggests, you can get the VC++ for each
extension to work with any arbitrary collection type by defining begin()
and end()
methods on the collection to return a custom iterator. Your iterator in turn has to implement the necessary interface (dereference operator, increment operator, etc). I've done this to wrap all of the MFC collection classes for legacy code. It's a bit of work, but can be done.
正如@yngum 所建议的那样,您可以通过在for each
集合上定义begin()
和end()
方法来返回自定义迭代器,从而使 VC++扩展能够处理任何任意集合类型。您的迭代器反过来必须实现必要的接口(取消引用运算符、增量运算符等)。我这样做是为了包装遗留代码的所有 MFC 集合类。这是一项工作,但可以完成。