在 c++11 中,迭代向量的最现代、最"right"的方法是什么
What is the most modern way, the most "right" way to iterate through a vector, in c++11
我现在正在学习c++,我看到了很多遍历向量的方法。我想知道,既然我想用正确的方法学习,那么现在最正确的迭代方法是什么?
取决于以下因素:
- 在循环中需要迭代器还是只需要元素?
- 你喜欢"几乎总是自动"(AAA)还是"几乎从不自动"(ANA)?
例如,如果您只想遍历std::vector<int> v
元素的一些副本,您可以这样做:
for (int element : v) {
// Use element
}
如果你是AAA级的人,你会写auto element
。
如果你想避免复制,你需要一个const引用:
for (int const &element : v) {
// Use element
}
如果你是AAA级玩家,你会写auto const &element
。
如果你想修改vector中的值,那么你需要一个可修改的引用:
for (int &element : v) {
// Use element
}
再说一遍,如果你喜欢AAA,那就换成auto &element
。
如果需要在循环内使用迭代器:
for (std::vector<int>::iterator it = std::begin(v); it != std::end(v); ++it) {
// Use it
}
如果你是AAA级开发者:
for (auto it = std::begin(v); it != std::end(v); ++it) {
// Use it
}
请注意,如果循环中的代码不依赖于实际类型(这不是很常见),ANA人员也会使用auto
。
还要注意,通常可以通过使用<algorithm>
中的适当算法来完全避免循环。关于这方面的一些信息,请查看Sean Parent的演讲。他建议任何超过几行的循环都是不好的,通常可以写得更整洁。
没有适用于所有上下文的正确方法。最简单的是for
循环:
std::vector<SomeClass> v;
for (SomeClass& sc : v)
sc.Method();
但在某些情况下std::for_each
可能更好,甚至是好的旧for(;;)
使用迭代器…它们是专门为此目的而设计的,适用于所有STL数据结构
http://www.cplusplus.com/reference/vector/vector/begin/^链接将帮助理解的例子
这取决于你为什么迭代。在很多情况下,你根本不应该迭代;使用一种标准算法。否则,如果您只想访问一个容器的所有元素,则范围for
是最佳解决方案。如果你想尽早中断迭代,那么最好使用普通的for
,除非(通常情况下)你需要在循环之后使用迭代器,在这种情况下,while
可能更好;在这两种情况下,都使用迭代器。最后,如果并行迭代多个向量,使用一个索引可能比使用多个迭代器更清晰。
我强烈建议使用range-for循环(从c++ 11开始)
vector<int> myVector {0, 1, 2, 3, 4};
for (const auto& i : myVector)
cout << i << endl;
Pre-C + + 11:
std::for_each(myVector.begin(), myVector.end(), myCallable);
c++ 14将不会添加任何关于此场景的内容;可能c++ 17会,使用并行循环。
如果您需要使用索引,您可以为
选择"raw"。for (auto i = 0; i < myVector.size(); ++i)
这些是最常见的方法。如果你需要更专业的东西,比如分类、移动、查找,看看<algorithm>
。
遍历向量的方式取决于迭代的目的。跟随的例子。
标准库算法
如果有一个标准库算法可以满足你的目的,那就使用它。这包括了当操作可以简单地使用lambda、函数或函子表示时的搜索、累积、转换等。通过提供适当的迭代器,它还允许您在您选择的范围内迭代,即不仅仅是begin
和end
。
示例:计算一个向量的前20个元素的和
std::vector<int> v = { /* fill as appropriate */ };
int sum_20 = std::accumulate(std::begin(v), std::begin(v)+20, 0);
示例:将每个元素Double并存储在另一个vector
中std::vector<int> v = { /* fill as appropriate */ };
std::vector<int> doubled;
doubled.reserve(v.size()); // avoid reallocations
std::transform(std::begin(v), std::end(v),
std::back_inserter(doubled), [](int x){return x*2;})
基于范围
的例子:
std::vector<int> v = { /* fill as appropriate */ };
for (int& x : v)
{
do_something_with(x);
do_another_thing_with(x);
if (some_condition(x))
{
do_something_else();
}
}
这对于遍历整个容器非常有效,在这种情况下,您想要执行的操作比您想要在一个简单的lambda中表示的操作更复杂,但又不足以(或使用多次)证明自己的函数。
在某种程度上,您还可以自定义访问元素的方式。如果元素很大,并且只读取它们,则可以指定const foo&
:
for (const MyBigObject& : MyBigContainer) { /* ... */ }
如果你需要修改它们,去掉const:
for (MyBigObject& : MyBigContainer) { /* ... */ }
对于小对象,或者需要副本的地方,省略引用:
for (int x : MyVecInt) { /* ... */ }
这允许你为你的访问指定最合适的constness,并控制你是通过引用访问还是通过值访问。通过提供const或非const迭代器,并为提供给接受可调用实体的算法的任何函数/函子/lambda提供适当的参数类型,标准库算法也可以完成类似的事情。
请注意,基于范围的for只会遍历整个容器,并且没有简单的方法可以同时遍历两个或多个容器。与…相比,这限制了它的适用性
对<<p> 常规/strong>如果需要同时遍历多个容器,或者需要访问索引或迭代器以及元素本身,常规的for循环可能是正确的方法。这是最灵活的,允许你自定义迭代的每一个方面,但它不会利用任何标准库优化,并且通常需要更多的样板来设置迭代器/索引对,检查你是否在容器的范围内,等等。
例子:
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = {2, 4, 6};
for (std::vector<int>::size_type idx = 0; idx < v1.size(); ++idx)
{
std::cout << v1[idx] * v2[idx] << ", ";
}
std::vector<int> v1 = {1, 2, 3};
auto iter = v1.cbegin();
const auto end = v1.cend() - 1;
for (; iter != end; ++iter)
{
std::cout << *iter + *(iter+1) << "n";
}
- MSVC是否支持C++11样式的属性而不是__declspec
- 创建LinkedList退出,返回代码为-11(SIGSEGV)
- 我可以将一个用clang c++11编译的对象与另一个用c++17编译的对象链接起来吗
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 如何将模板转换为C++11之前的模板
- c++11评估顺序(未定义的行为)
- C++中的VLA,扩展名为std=C++11
- 代码在我的计算机上运行良好,但是在将其提交给coursera时遇到未知的信号11问题
- "类模板示例<int>;"语句对 C++11 是什么意思?
- this_thread::sleep_for和计时时钟之间的关系是否由C++11标准指定
- 如何使用lock_guard在c++11中实现scoped_lock功能
- C++11 中不同类型的对象的 std::array 的替代方案
- 为什么 -mmacosx-version-min=10.10 不阻止使用标记为从 10.11 开始的函数?
- 为什么我的C++代码中出现'Segmentation Fault: 11'行?
- 强枚举类型定义:Clang Bug 还是 C++11 标准不确定性?
- 别名模板的专业化 C++11 中没有开销的最佳替代方案
- STLPort using C++11
- Qt 5.11.2 (Clang 8.0 (Apple), 64 位), 找不到 QJSEngine 文件
- 在 C++11 中,如何查找并返回以给定字符串开头的字符串向量中的所有项?
- 在 c++11 中,迭代向量的最现代、最"right"的方法是什么