如何使 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:25:20  来源:igfitidea点击:

How to make the for each loop function in C++ work with a custom class

c++listc++11foreach

提问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* vectormember 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 forin 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-likeloop?

我的问题是:有没有办法让这个自定义 List 类与foreach-like循环一起工作?

回答by Yuushi

Firstly, the syntax of a for-eachloop 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-eachloop, you need to provide a begin()and an end()function. These are generally overloaded, returning either an iteratoror 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 iterablebe of type Iterable. Then, in order to make

iterable是类型Iterable。然后,为了使

for (Type x : iterable)

compile, there must be types called Typeand ITypeand there must be functions

编译,必须有被调用的类型TypeIType并且必须有函数

IType Iterable::begin()
IType Iterable::end()

ITypemust 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 Typeor Type&) can be used, which will have the expected implications (constness, reference-instead-of-copy etc.).

其中代替Type,可以使用任何兼容类型(例如const TypeType&),这将具有预期的含义(一致性,引用代替复制等)。

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& rhsas needed.

由于整个扩展在语法上发生,您还可以稍微更改运算符的声明,例如让 *it 返回一个引用或让 != take aconst IType& rhs根据需要。

Note that you cannot use the for (Type& x : iterable)form if *itdoes 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, continueand breakwill 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_eachloop 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 eachextension 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 集合类。这是一项工作,但可以完成。