Namespaces
Variants

Range-based for loop (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
for
range- for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Ejecuta un bucle for sobre un rango.

Se utiliza como un equivalente más legible del tradicional for loop que opera sobre un rango de valores, como todos los elementos en un contenedor.

Contenidos

Sintaxis

attr  (opcional) for ( init-statement  (opcional) item-declaration : range-initializer ) statement
attr - cualquier número de atributos
init-statement - (desde C++20) uno de
(desde C++23)

Nótese que cualquier init-statement debe terminar con un punto y coma. Por esto se describe informalmente como una expresión o una declaración seguida de un punto y coma.

item-declaration - una declaración para cada elemento del rango
range-initializer - una expresión o lista de inicialización entre llaves
statement - cualquier sentencia (normalmente una sentencia compuesta)

Explicación

La sintaxis anterior produce código equivalente al siguiente excepto por la extensión de vida útil de los temporales del range-initializer (ver abajo ) (desde C++23) (las variables y expresiones envueltas en /* */ son solo para exposición):

{

auto&& /* rango */ = inicializador-de-rango  ;
for (auto /* inicio */ = /* expresión-inicio */ , /* fin */ = /* expresión-fin */ ;
/* inicio */ != /* fin */ ; ++ /* inicio */ )
{
declaración-elemento = * /* inicio */ ;
sentencia
}

}

(hasta C++17)

{

auto&& /* rango */ = inicializador-de-rango  ;
auto /* inicio */ = /* expresión-inicio */ ;
auto /* fin */ = /* expresión-fin */ ;
for ( ; /* inicio */ != /* fin */ ; ++ /* inicio */ )
{
declaración-elemento = * /* inicio */ ;
sentencia
}

}

(desde C++17)
(hasta C++20)

{

sentencia-inicialización
auto&& /* rango */ = inicializador-de-rango  ;
auto /* inicio */ = /* expresión-inicio */ ;
auto /* fin */ = /* expresión-fin */ ;
for ( ; /* inicio */ != /* fin */ ; ++ /* inicio */ )
{
declaración-elemento = * /* inicio */ ;
sentencia
}

}

(desde C++20)

range-initializer se evalúa para inicializar la secuencia o rango a iterar. Cada elemento de la secuencia, a su vez, se desreferencia y se utiliza para inicializar la variable con el tipo y nombre dados en item-declaration .

item-declaration puede ser uno de los siguientes:

Expresiones solo de exposición /* begin-expr */ y /* end-expr */ se definen de la siguiente manera:

  • Si el tipo de /* range */ es una referencia a un tipo de arreglo R :
  • Si R es de límite N , /* begin-expr */ es /* range */ y /* end-expr */ es /* range */ + N .
  • Si R es un array de límite desconocido o un array de tipo incompleto, el programa está mal formado.
  • Si el tipo de /* range */ es una referencia a un tipo de clase C , y las búsquedas en el ámbito de C para los nombres " begin " y " end " encuentran al menos una declaración cada una, entonces /* begin-expr */ es /* range */ . begin ( ) y /* end-expr */ es /* range */ . end ( ) .
  • En caso contrario, /* begin-expr */ es begin ( /* range */ ) y /* end-expr */ es end ( /* range */ ) , donde " begin " y " end " se encuentran mediante búsqueda dependiente de argumento (no se realiza búsqueda no-ADL).

Si el bucle necesita ser terminado dentro de statement , se puede usar una break statement como sentencia de terminación.

Si la iteración actual necesita ser terminada dentro de statement , se puede usar una continue statement como atajo.

Si un nombre introducido en init-statement es redeclarado en el bloque más externo de statement , el programa está mal formado:

for (int i : {1, 2, 3})
    int i = 1; // error: redeclaración

Inicializador de rango temporal

Si range-initializer retorna un temporal, su duración se extiende hasta el final del bucle, como se indica al vincularlo a la referencia de reenvío /* range */ .

Las vidas de todos los temporales dentro del range-initializer no se extienden a menos que de otro modo serían destruidos al final del range-initializer (since C++23) .

// si foo() retorna por valor
for (auto& x : foo().items()) { /* ... */ } // hasta C++23 comportamiento indefinido

Este problema puede solucionarse utilizando init-statement :

for (T thing = foo(); auto& x : thing.items()) { /* ... */ } // OK
(desde C++20)


Nótese que incluso en C++23 los parámetros no referencia de llamadas de función intermedias no obtienen una extensión de vida útil (porque en algunas ABIs se destruyen en la función llamada, no en la llamadora), pero eso solo es un problema para funciones que de todos modos tienen errores:

using T = std::list<int>;
const T& f1(const T& t) { return t; }
const T& f2(T t)        { return t; } // always returns a dangling reference
T g();
void foo()
{
    for (auto e : f1(g())) {} // OK: lifetime of return value of g() extended
    for (auto e : f2(g())) {} // UB: lifetime of f2's value parameter ends early
}
(desde C++23)

Notas

Si el range-initializer es una lista de inicialización entre llaves , /* range */ se deduce como una referencia a un std::initializer_list .

Es seguro, y de hecho, preferible en código genérico, usar deducción para referencias de reenvío, for ( auto && var : sequence ) .

La interpretación de miembro se utiliza si el tipo de rango tiene un miembro llamado " begin " y un miembro llamado " end ". Esto se hace independientemente de si el miembro es un tipo, miembro de datos, función o enumerador, y sin importar su accesibilidad. Por lo tanto, una clase como class meow { enum { begin = 1 , end = 2 } ; /* resto de la clase */ } ; no puede utilizarse con el bucle for basado en rangos incluso si las funciones " begin "/" end " en el ámbito del espacio de nombres están presentes.

Aunque la variable declarada en la item-declaration normalmente se utiliza en la statement , no es obligatorio hacerlo.

A partir de C++17, los tipos de /* begin-expr */ y /* end-expr */ no tienen que ser iguales, y de hecho el tipo de /* end-expr */ no tiene que ser un iterador: solo necesita poder compararse por desigualdad con uno. Esto hace posible delimitar un rango mediante un predicado (por ejemplo, "el iterador apunta a un carácter nulo").

(since C++17)

Cuando se utiliza con un objeto (no constante) que tiene semántica de copia en escritura, el bucle for basado en rangos puede desencadenar una copia profunda al llamar (implícitamente) a la función miembro no constante begin() .

Si eso es indeseable (por ejemplo, porque el bucle no está modificando realmente el objeto), puede evitarse usando std::as_const :

struct cow_string { /* ... */ }; // a copy-on-write string
cow_string str = /* ... */;
// for (auto x : str) { /* ... */ } // may cause deep copy
for (auto x : std::as_const(str)) { /* ... */ }
(desde C++17)
Macro de prueba de características Valor Estándar Característica
__cpp_range_based_for 200907L (C++11) Bucle for basado en rangos
201603L (C++17) Bucle for basado en rangos con tipos diferentes de begin / end
202211L (C++23) Extensión de vida útil para todos los objetos temporales en inicializador-de-rango

Palabras clave

for

Ejemplo

#include <iostream>
#include <vector>
int main()
{
    std::vector<int> v = {0, 1, 2, 3, 4, 5};
    for (const int& i : v) // acceso por referencia constante
        std::cout << i << ' ';
    std::cout << '\n';
    for (auto i : v) // acceso por valor, el tipo de i es int
        std::cout << i << ' ';
    std::cout << '\n';
    for (auto&& i : v) // acceso por referencia de reenvío, el tipo de i es int&
        std::cout << i << ' ';
    std::cout << '\n';
    const auto& cv = v;
    for (auto&& i : cv) // acceso por referencia f-d, el tipo de i es const int&
        std::cout << i << ' ';
    std::cout << '\n';
    for (int n : {0, 1, 2, 3, 4, 5}) // el inicializador puede ser una
                                     // lista de inicialización entre llaves
        std::cout << n << ' ';
    std::cout << '\n';
    int a[] = {0, 1, 2, 3, 4, 5};
    for (int n : a) // el inicializador puede ser un array
        std::cout << n << ' ';
    std::cout << '\n';
    for ([[maybe_unused]] int n : a)  
        std::cout << 1 << ' '; // la variable del bucle no necesita ser usada
    std::cout << '\n';
    for (auto n = v.size(); auto i : v) // la sentencia de inicialización (C++20)
        std::cout << --n + i << ' ';
    std::cout << '\n';
    for (typedef decltype(v)::value_type elem_t; elem_t i : v)
    // declaración typedef como sentencia de inicialización (C++20)
        std::cout << i << ' ';
    std::cout << '\n';
    for (using elem_t = decltype(v)::value_type; elem_t i : v)
    // declaración de alias como sentencia de inicialización (C++23)
        std::cout << i << ' ';
    std::cout << '\n';
}

Salida:

0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
1 1 1 1 1 1 
5 5 5 5 5 5 
0 1 2 3 4 5 
0 1 2 3 4 5

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.

DR Se aplica a Comportamiento publicado Comportamiento correcto
CWG 1442 C++11 no estaba especificado si la búsqueda de no-miembros
" begin " y " end " incluye la búsqueda no calificada habitual
no se realiza búsqueda no calificada habitual
CWG 2220 C++11 los nombres introducidos en init-statement podían ser redeclarados el programa está mal formado en este caso
CWG 2825 C++11 si range-initializer es una lista de inicialización entre llaves,
se buscarán los no-miembros " begin " y " end "
buscará miembros " begin "
y " end " en este caso
P0962R1 C++11 se usaba la interpretación de miembro si cualquiera
de los miembros " begin " y " end " estaba presente
solo se usa si ambos están presentes

Véase también

aplica un function object unario a elementos de un range
(plantilla de función)