I'm always trying to learn more about the languages I use (different styles, frameworks, patterns, etc). I've noticed that I never use std::for_each
so I thought that perhaps I should start. The goal in such cases is to expand my mind and not to improve the code in some measure (readability, expressiveness, compactness, etc).
我一直在努力学习我使用的语言(不同的风格、框架、模式等)。我注意到我从不使用std::for_each,所以我想我应该开始了。这种情况下的目标是扩展我的思想,而不是在某种程度上改进代码(可读性、表达性、紧凑性等)。
So with that context in mind, is a good idea to use std::for_each
for simple tasks like, say, printing out a vector:
考虑到这个背景,使用std::for_each来完成一些简单的任务,比如打印出一个向量:
for_each(v.begin(), v.end(), [](int n) { cout << n << endl; }
(The [](int n)
being a lambda function). Instead of:
([](int n)是一个lambda函数)而不是:
for(int i=0; i<v.size(); i++) { cout << v[i] << endl; }
I hope this question doesn't seem pointless. I guess it almost asks a larger question... should an intermediate programmer use a language feature even though he doesn't really need to at this time but just so that he can understand the feature better for a time that may actually greatly benefit from it. Although this larger question has probably already been asked (e.g. here).
我希望这个问题不会显得毫无意义。我猜它几乎提出了一个更大的问题……中级程序员是否应该使用语言特性,即使他现在不需要,只是为了在一段时间内更好地理解这个特性,这实际上可能会使他从中获益良多。虽然这个更大的问题可能已经被提出(例如这里)。
9 个解决方案
#1
46
There is an advantage to using std::for_each
instead of an old school for
loop (or even the newfangled C++0x range-for
loop): you can look at the first word of the statement and you know exactly what the statement does.
使用std::for_each而不是使用旧的for循环(甚至是最新的c++ 0x range-for循环)有一个优点:您可以查看语句的第一个单词,并且确切地知道语句的作用。
When you see the for_each
, you know that the operation in the lambda is performed exactly once for each element in the range (assuming no exceptions are thrown). It isn't possible to break out of the loop early before every element has been processed and it isn't possible to skip elements or evaluate the body of the loop for one element multiple times.
当您看到for_each时,您知道lambda中的操作对范围中的每个元素执行一次(假设没有抛出异常)。在每个元素都被处理之前,不可能在早期就跳出循环,也不可能多次跳过元素或计算一个元素的循环体。
With the for
loop, you have to read the entire body of the loop to know what it does. It may have continue
, break
, or return
statements in it that alter the control flow. It may have statements that modify the iterator or index variable(s). There is no way to know without examining the entire loop.
对于for循环,您必须读取整个循环体以了解它的功能。它可能有继续、中断或返回语句来改变控制流。它可能有修改迭代器或索引变量的语句。如果不检查整个循环,就无法知道。
Herb Sutter discussed the advantages of using algorithms and lambda expressions in a recent presentation to the Northwest C++ Users Group.
Herb Sutter在最近一次面向西北c++用户组的演讲中讨论了使用算法和lambda表达式的好处。
Note that you can actually use the std::copy
algorithm here if you'd prefer:
注意,如果您愿意,您可以在这里使用std::copy算法:
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, "\n"));
#2
24
It depends.
视情况而定。
The power of for_each
is, that you can use it with any container whose iterators satisfy the input iterator concept and as such it's generically useable on any container. That increases maintainability in a way that you can just swap out the container and don't need to change anything. The same doesn't hold true for a loop over the size
of a vector. The only other containers you could swap it with without having to change the loop would be another random-access one.
for_each的强大之处在于,您可以对任何迭代器满足输入迭代器概念的容器使用它,因此它在任何容器上都是通用的。这增加了可维护性,您可以将容器替换掉,不需要改变任何东西。对于一个向量大小的循环来说,这是不成立的。您可以交换它而不必更改循环的其他容器将是另一个随机访问容器。
Now, if you'd type out the iterator version yourself, the typical version looks like this:
现在,如果您自己输入迭代器版本,典型的版本是这样的:
// substitute 'container' with a container of your choice
for(std::container<T>::iterator it = c.begin(); it != c.end(); ++it){
// ....
}
Rather lengthy, eh? C++0x relieves us of that length thing with the auto
keyword:
而漫长的,不是吗?c++ 0x使用auto关键字来缓解我们的长度问题:
for(auto it = c.begin(); it != c.end(); ++it){
// ....
}
Already nicer, but still not perfect. You're calling end
on every iteration and that can be done better:
已经很好了,但还不完美。每次迭代都调用end,这样可以做得更好:
for(auto it = c.begin(), ite = c.end(); it != ite; ++it){
// ....
}
Looks good now. Still, longer than the equivalent for_each
version:
现在看起来不错。不过,比等效的for_each版本要长:
std::for_each(c.begin(), c.end(), [&](T& item){
// ...
});
With "equivalent" being slightly subjective, as the T
in the parameter list of the lambda could be some verbose type like my_type<int>::nested_type
. Though, one can typedef
his/her way around that. Honestly, I still don't understand why lambdas weren't allowed to be polymorphic with type deduction...
由于“等效”有点主观,因为lambda参数列表中的T可能是一些详细类型,如my_type
Now, another thing to consider is that for_each
, the name itself, already expresses an intent. It says that no elements in the sequence will be skipped, which might be the case with your normal for-loop.
现在,要考虑的另一件事是for_each,名称本身,已经表达了一个意图。它表示序列中的任何元素都不会被跳过,这可能是正常的for循环的情况。
That brings me to another point: Since for_each
is intended to run over the whole sequence and apply an operation on every item in the container, it is not designed to handle early return
s or break
s in general. continue
can be simulated with a return
statement from the lambda / functor.
这让我想到了另一点:由于for_each打算遍历整个序列,并对容器中的每个项应用一个操作,所以它并不打算处理早期的返回或中断。继续可以使用lambda / functor的返回语句进行模拟。
So, use for_each
where you really want to apply an operation on every item in the collection.
因此,如果您确实想对集合中的每个项应用操作,请使用for_each。
On a side note, for_each
might just be "deprecated" with C++0x thanks to the awesome range-based for-loops (also called foreach loops):
顺便说一句,for_each可能只是“不赞成”使用c++ 0x,因为它有很棒的基于范围的for循环(也称为foreach循环):
for(auto& item : container){
// ...
}
Which is way shorter (yay) and allows all three options of:
这条路更短(yay),并允许以下三个选项:
- returning early (even with a return value!)
- 提前返回(即使有返回值!)
- breaking out of the loop and
- 跳出循环
- skipping over some elements.
- 跳过一些元素。
#3
9
I generally would recommend use of std::for_each
. Your example for loop does not work for non-random-access containers. You can write the same loop using iterators, but it's usually a pain due to writing out std::SomeContainerName<SomeReallyLongUserType>::const_iterator
as the type of the iteration variable. std::for_each
insulates you from this, and also amortizes the call to end
automatically.
我通常建议使用std::for_each。循环的示例不适用于非随机访问容器。您可以使用迭代器编写相同的循环,但是由于将std::SomeContainerName
#4
8
IMHO, you should try this new features in your test code.
IMHO,您应该在测试代码中尝试这个新特性。
In the production code you should try the features which you feel comfortable with. (i.e. if you feel comfortable with for_each
, you can use it.)
在产品代码中,您应该尝试您觉得舒服的特性。(也就是说,如果你喜欢for_each,你可以使用它。)
#5
3
for_each
is the most general of the algorithms that iterate over a sequence, and thus the least expressive. If the goal of the iteration can be expressed in terms of transform
, accumulate
, copy
, I feel that it's better to use the specific algorithm rather than the generic for_each
.
for_each是迭代序列的最通用的算法,因此它的表达能力最低。如果迭代的目标可以用转换、累积、复制来表示,我认为最好使用特定的算法而不是通用的for_each。
With the new C++0x range for (supported in gcc 4.6.0, try that out!), for_each
might even lose its niche of being the most generic way to apply a function to a sequence.
使用新的c++ 0x范围for(在gcc 4.6.0中得到了支持,请尝试一下!)
#6
1
You can use for
loop scoping C++11
可以使用for循环遍历c++ 11。
For example:
例如:
T arr[5];
for (T & x : arr) //use reference if you want write data
{
//something stuff...
}
Where T is every type you want.
这里T是你想要的所有类型。
It work for every containers in STL and classic arrays.
它适用于STL和经典数组中的每个容器。
#7
0
Well... that works, but for printing a vector (or the content of other container types) I prefer this :
嗯…这是可行的,但对于打印一个向量(或其他容器类型的内容),我更喜欢这样:
std::copy(v.begin(), v.end(), std::ostream_iterator< int >( std::cout, " " ) );
#8
0
Boost.Range simplifies the use of standard algorithms. For your example you could write:
提振。范围简化了标准算法的使用。例如,您可以这样写:
boost::for_each(v, [](int n) { cout << n << endl; });
(or boost::copy
with an ostream iterator as suggested in other answers).
(或boost:::使用ostream迭代器进行复制,如其他答案所示)。
#9
0
Note that the "traditional" example is buggy:
注意,“传统”示例有缺陷:
for(int i=0; i<v.size(); i++) { cout << v[i] << endl; }
This assumes that int
can always represent the index of every value in the vector. There are actually two ways this can go wrong.
这假设int总是可以表示向量中每个值的索引。实际上有两种方法会出错。
One is that int
may be of lower rank than std::vector<T>::size_type
. On a 32-bit machine, int
s are typically 32-bits wide but v.size()
will almost certainly be 64 bits wide. If you manage to stuff 2^32 elements into the vector, your index will never reach the end.
一个是int可能比std::vector
The second problem is that you're comparing a signed value (int
) to an unsigned value (std::vector<T>::size_type
). So even if they were of the same rank, when the size exceeds the maximum integer value, then the index will overflow and trigger undefined behavior.
第二个问题是您正在比较一个有符号值(int)和一个无符号值(std::vector
You may have prior knowledge that, for this vector, those error conditions will never be true. But you'd either have to ignore or disable the compiler warnings. And if you disable them, then you don't get the benefit of those warnings helping you find actual bugs elsewhere in your code. (I've spent lots of time tracking down bugs that should have been detected by these compiler warnings, if the code had made it feasible to enable them.)
你可能事先知道,对于这个向量,这些错误条件永远不会成立。但是您必须忽略或禁用编译器警告。如果您禁用它们,那么您将无法从这些警告中获益,这些警告将帮助您在代码中的其他地方找到真正的bug。(我花了很多时间来跟踪那些本应该被这些编译器警告检测到的错误,如果代码使它们成为可能的话。)
So, yes, for_each
(or any appropriate <algorithm>
) is better because it avoids this pernicious abuse of int
s. You could also use a range-based for loop or an iterator-based loop with auto.
所以,是的,for_each(或任何适当的 <算法> )更好,因为它避免了对int的有害滥用。您还可以使用基于范围的for循环或使用auto的基于迭代器的循环。
An additional advantage to using <algorithm>
s or iterators rather than indexes is that it gives you more flexibility to change container types in the future without refactoring all the code that uses it.
使用
#1
46
There is an advantage to using std::for_each
instead of an old school for
loop (or even the newfangled C++0x range-for
loop): you can look at the first word of the statement and you know exactly what the statement does.
使用std::for_each而不是使用旧的for循环(甚至是最新的c++ 0x range-for循环)有一个优点:您可以查看语句的第一个单词,并且确切地知道语句的作用。
When you see the for_each
, you know that the operation in the lambda is performed exactly once for each element in the range (assuming no exceptions are thrown). It isn't possible to break out of the loop early before every element has been processed and it isn't possible to skip elements or evaluate the body of the loop for one element multiple times.
当您看到for_each时,您知道lambda中的操作对范围中的每个元素执行一次(假设没有抛出异常)。在每个元素都被处理之前,不可能在早期就跳出循环,也不可能多次跳过元素或计算一个元素的循环体。
With the for
loop, you have to read the entire body of the loop to know what it does. It may have continue
, break
, or return
statements in it that alter the control flow. It may have statements that modify the iterator or index variable(s). There is no way to know without examining the entire loop.
对于for循环,您必须读取整个循环体以了解它的功能。它可能有继续、中断或返回语句来改变控制流。它可能有修改迭代器或索引变量的语句。如果不检查整个循环,就无法知道。
Herb Sutter discussed the advantages of using algorithms and lambda expressions in a recent presentation to the Northwest C++ Users Group.
Herb Sutter在最近一次面向西北c++用户组的演讲中讨论了使用算法和lambda表达式的好处。
Note that you can actually use the std::copy
algorithm here if you'd prefer:
注意,如果您愿意,您可以在这里使用std::copy算法:
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, "\n"));
#2
24
It depends.
视情况而定。
The power of for_each
is, that you can use it with any container whose iterators satisfy the input iterator concept and as such it's generically useable on any container. That increases maintainability in a way that you can just swap out the container and don't need to change anything. The same doesn't hold true for a loop over the size
of a vector. The only other containers you could swap it with without having to change the loop would be another random-access one.
for_each的强大之处在于,您可以对任何迭代器满足输入迭代器概念的容器使用它,因此它在任何容器上都是通用的。这增加了可维护性,您可以将容器替换掉,不需要改变任何东西。对于一个向量大小的循环来说,这是不成立的。您可以交换它而不必更改循环的其他容器将是另一个随机访问容器。
Now, if you'd type out the iterator version yourself, the typical version looks like this:
现在,如果您自己输入迭代器版本,典型的版本是这样的:
// substitute 'container' with a container of your choice
for(std::container<T>::iterator it = c.begin(); it != c.end(); ++it){
// ....
}
Rather lengthy, eh? C++0x relieves us of that length thing with the auto
keyword:
而漫长的,不是吗?c++ 0x使用auto关键字来缓解我们的长度问题:
for(auto it = c.begin(); it != c.end(); ++it){
// ....
}
Already nicer, but still not perfect. You're calling end
on every iteration and that can be done better:
已经很好了,但还不完美。每次迭代都调用end,这样可以做得更好:
for(auto it = c.begin(), ite = c.end(); it != ite; ++it){
// ....
}
Looks good now. Still, longer than the equivalent for_each
version:
现在看起来不错。不过,比等效的for_each版本要长:
std::for_each(c.begin(), c.end(), [&](T& item){
// ...
});
With "equivalent" being slightly subjective, as the T
in the parameter list of the lambda could be some verbose type like my_type<int>::nested_type
. Though, one can typedef
his/her way around that. Honestly, I still don't understand why lambdas weren't allowed to be polymorphic with type deduction...
由于“等效”有点主观,因为lambda参数列表中的T可能是一些详细类型,如my_type
Now, another thing to consider is that for_each
, the name itself, already expresses an intent. It says that no elements in the sequence will be skipped, which might be the case with your normal for-loop.
现在,要考虑的另一件事是for_each,名称本身,已经表达了一个意图。它表示序列中的任何元素都不会被跳过,这可能是正常的for循环的情况。
That brings me to another point: Since for_each
is intended to run over the whole sequence and apply an operation on every item in the container, it is not designed to handle early return
s or break
s in general. continue
can be simulated with a return
statement from the lambda / functor.
这让我想到了另一点:由于for_each打算遍历整个序列,并对容器中的每个项应用一个操作,所以它并不打算处理早期的返回或中断。继续可以使用lambda / functor的返回语句进行模拟。
So, use for_each
where you really want to apply an operation on every item in the collection.
因此,如果您确实想对集合中的每个项应用操作,请使用for_each。
On a side note, for_each
might just be "deprecated" with C++0x thanks to the awesome range-based for-loops (also called foreach loops):
顺便说一句,for_each可能只是“不赞成”使用c++ 0x,因为它有很棒的基于范围的for循环(也称为foreach循环):
for(auto& item : container){
// ...
}
Which is way shorter (yay) and allows all three options of:
这条路更短(yay),并允许以下三个选项:
- returning early (even with a return value!)
- 提前返回(即使有返回值!)
- breaking out of the loop and
- 跳出循环
- skipping over some elements.
- 跳过一些元素。
#3
9
I generally would recommend use of std::for_each
. Your example for loop does not work for non-random-access containers. You can write the same loop using iterators, but it's usually a pain due to writing out std::SomeContainerName<SomeReallyLongUserType>::const_iterator
as the type of the iteration variable. std::for_each
insulates you from this, and also amortizes the call to end
automatically.
我通常建议使用std::for_each。循环的示例不适用于非随机访问容器。您可以使用迭代器编写相同的循环,但是由于将std::SomeContainerName
#4
8
IMHO, you should try this new features in your test code.
IMHO,您应该在测试代码中尝试这个新特性。
In the production code you should try the features which you feel comfortable with. (i.e. if you feel comfortable with for_each
, you can use it.)
在产品代码中,您应该尝试您觉得舒服的特性。(也就是说,如果你喜欢for_each,你可以使用它。)
#5
3
for_each
is the most general of the algorithms that iterate over a sequence, and thus the least expressive. If the goal of the iteration can be expressed in terms of transform
, accumulate
, copy
, I feel that it's better to use the specific algorithm rather than the generic for_each
.
for_each是迭代序列的最通用的算法,因此它的表达能力最低。如果迭代的目标可以用转换、累积、复制来表示,我认为最好使用特定的算法而不是通用的for_each。
With the new C++0x range for (supported in gcc 4.6.0, try that out!), for_each
might even lose its niche of being the most generic way to apply a function to a sequence.
使用新的c++ 0x范围for(在gcc 4.6.0中得到了支持,请尝试一下!)
#6
1
You can use for
loop scoping C++11
可以使用for循环遍历c++ 11。
For example:
例如:
T arr[5];
for (T & x : arr) //use reference if you want write data
{
//something stuff...
}
Where T is every type you want.
这里T是你想要的所有类型。
It work for every containers in STL and classic arrays.
它适用于STL和经典数组中的每个容器。
#7
0
Well... that works, but for printing a vector (or the content of other container types) I prefer this :
嗯…这是可行的,但对于打印一个向量(或其他容器类型的内容),我更喜欢这样:
std::copy(v.begin(), v.end(), std::ostream_iterator< int >( std::cout, " " ) );
#8
0
Boost.Range simplifies the use of standard algorithms. For your example you could write:
提振。范围简化了标准算法的使用。例如,您可以这样写:
boost::for_each(v, [](int n) { cout << n << endl; });
(or boost::copy
with an ostream iterator as suggested in other answers).
(或boost:::使用ostream迭代器进行复制,如其他答案所示)。
#9
0
Note that the "traditional" example is buggy:
注意,“传统”示例有缺陷:
for(int i=0; i<v.size(); i++) { cout << v[i] << endl; }
This assumes that int
can always represent the index of every value in the vector. There are actually two ways this can go wrong.
这假设int总是可以表示向量中每个值的索引。实际上有两种方法会出错。
One is that int
may be of lower rank than std::vector<T>::size_type
. On a 32-bit machine, int
s are typically 32-bits wide but v.size()
will almost certainly be 64 bits wide. If you manage to stuff 2^32 elements into the vector, your index will never reach the end.
一个是int可能比std::vector
The second problem is that you're comparing a signed value (int
) to an unsigned value (std::vector<T>::size_type
). So even if they were of the same rank, when the size exceeds the maximum integer value, then the index will overflow and trigger undefined behavior.
第二个问题是您正在比较一个有符号值(int)和一个无符号值(std::vector
You may have prior knowledge that, for this vector, those error conditions will never be true. But you'd either have to ignore or disable the compiler warnings. And if you disable them, then you don't get the benefit of those warnings helping you find actual bugs elsewhere in your code. (I've spent lots of time tracking down bugs that should have been detected by these compiler warnings, if the code had made it feasible to enable them.)
你可能事先知道,对于这个向量,这些错误条件永远不会成立。但是您必须忽略或禁用编译器警告。如果您禁用它们,那么您将无法从这些警告中获益,这些警告将帮助您在代码中的其他地方找到真正的bug。(我花了很多时间来跟踪那些本应该被这些编译器警告检测到的错误,如果代码使它们成为可能的话。)
So, yes, for_each
(or any appropriate <algorithm>
) is better because it avoids this pernicious abuse of int
s. You could also use a range-based for loop or an iterator-based loop with auto.
所以,是的,for_each(或任何适当的 <算法> )更好,因为它避免了对int的有害滥用。您还可以使用基于范围的for循环或使用auto的基于迭代器的循环。
An additional advantage to using <algorithm>
s or iterators rather than indexes is that it gives you more flexibility to change container types in the future without refactoring all the code that uses it.
使用