基于范围的
Version >= C++ 11
for
循环可用于迭代基于迭代器的范围的元素,而无需使用数字索引或直接访问迭代器:
vector<float> v = {0.4f, 12.5f, 16.234f};
for(auto val: v)
{
std::cout << val << " ";
}
std::cout << std::endl;
这将迭代 v
中的每个元素,val
获取当前元素的值。以下声明:
for (for-range-declaration : for-range-initializer ) statement
相当于:
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr, __end = end-expr;
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
Version >= C++ 17
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr;
auto __end = end-expr; // end is allowed to be a different type than begin in C++17
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
这一变化是为 C++ 20 中 Ranges TS 的计划支持而引入的。
在这种情况下,我们的循环相当于:
{
auto&& __range = v;
auto __begin = v.begin(), __end = v.end();
for (; __begin != __end; ++__begin) {
auto val = *__begin;
std::cout << val << " ";
}
}
请注意,auto val
声明了一个值类型,它将是存储在该范围内的值的副本(我们正在从迭代器中复制初始化它)。如果存储在该范围内的值复制起来很昂贵,你可能需要使用 const auto &val
。你也不需要使用 auto
; 你可以使用适当的类型名称,只要它可以从范围的值类型中隐式转换。
如果你需要访问迭代器,基于范围的对你来说无法帮助你(至少不需要付出一些努力)。
如果你想参考它,你可以这样做:
vector<float> v = {0.4f, 12.5f, 16.234f};
for(float &val: v)
{
std::cout << val << " ";
}
如果你有 const
容器,你可以迭代 const
参考:
const vector<float> v = {0.4f, 12.5f, 16.234f};
for(const float &val: v)
{
std::cout << val << " ";
}
当序列迭代器返回代理对象并且你需要以非 const
方式对该对象进行操作时,可以使用转发引用。注意:它很可能会使读者感到困惑。
vector<bool> v(10);
for(auto&& val: v)
{
val = true;
}
提供给基于范围的 for
的范围类型可以是以下之一:
-
语言数组:
float arr[] = {0.4f, 12.5f, 16.234f}; for(auto val: arr) { std::cout << val << " "; }
请注意,分配动态数组不计算:
float *arr = new float[3]{0.4f, 12.5f, 16.234f}; for(auto val: arr) //Compile error. { std::cout << val << " "; }
-
任何具有成员函数
begin()
和end()
的类型,它将迭代器返回给该类型的元素。标准库容器符合条件,但也可以使用用户定义的类型:struct Rng { float arr[3]; // pointers are iterators const float* begin() const {return &arr[0];} const float* end() const {return &arr[3];} float* begin() {return &arr[0];} float* end() {return &arr[3];} }; int main() { Rng rng = {{0.4f, 12.5f, 16.234f}}; for(auto val: rng) { std::cout << val << " "; } }
-
任何具有非成员
begin(type)
和end(type)
函数的类型,可以通过参数依赖查找找到,基于type
。这对于创建范围类型非常有用,而无需修改类类型本身:namespace Mine { struct Rng {float arr[3];}; // pointers are iterators const float* begin(const Rng &rng) {return &rng.arr[0];} const float* end(const Rng &rng) {return &rng.arr[3];} float* begin(Rng &rng) {return &rng.arr[0];} float* end(Rng &rng) {return &rng.arr[3];} } int main() { Mine::Rng rng = {{0.4f, 12.5f, 16.234f}}; for(auto val: rng) { std::cout << val << " "; } }