C++ Range-Based For


Esempio

C ++ 11

for cicli for possono essere utilizzati per scorrere gli elementi di un intervallo basato su iteratore, senza utilizzare un indice numerico o accedere direttamente agli iteratori:

vector<float> v = {0.4f, 12.5f, 16.234f};

for(auto val: v)
{
    std::cout << val << " ";
}

std::cout << std::endl;

Questo itererà su ogni elemento in v , con val ottiene il valore dell'elemento corrente. La seguente dichiarazione:

for (for-range-declaration : for-range-initializer ) statement

è equivalente a:

{
    auto&& __range = for-range-initializer;
    auto __begin = begin-expr, __end = end-expr;
    for (; __begin != __end; ++__begin) {
        for-range-declaration = *__begin;
        statement
    }
}
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
    }
}

Questa modifica è stata introdotta per il supporto pianificato di Ranges TS in C ++ 20.

In questo caso, il nostro ciclo è equivalente a:

{
    auto&& __range = v;
    auto __begin = v.begin(), __end = v.end();
    for (; __begin != __end; ++__begin) {
        auto val = *__begin;
        std::cout << val << " ";
    }
}

Si noti che auto val dichiara un tipo di valore, che sarà una copia di un valore memorizzato nell'intervallo (lo stiamo inizializzando in copia dall'iteratore mentre procediamo). Se i valori memorizzati nell'intervallo sono costosi da copiare, è possibile utilizzare const auto &val . Inoltre, non è necessario utilizzare l' auto ; è possibile utilizzare un nome di tipo appropriato, purché sia ​​implicitamente convertibile dal tipo di valore dell'intervallo.

Se hai bisogno di accedere all'iteratore, il range-based non può aiutarti (non senza alcuno sforzo, almeno).

Se desideri fare riferimento, puoi farlo:

vector<float> v = {0.4f, 12.5f, 16.234f};

for(float &val: v)
{
    std::cout << val << " ";
}

È possibile eseguire l'iterazione sul riferimento const se si dispone di un contenitore const :

const vector<float> v = {0.4f, 12.5f, 16.234f};

for(const float &val: v)
{
    std::cout << val << " ";
}

Uno userebbe i riferimenti di inoltro quando l'iteratore di sequenza restituisce un oggetto proxy ed è necessario operare su quell'oggetto in modo non const . Nota: molto probabilmente confonderà i lettori del tuo codice.

vector<bool> v(10);

for(auto&& val: v)
{
    val = true;
}

Il tipo "range" fornito alla gamma-base for può essere uno dei seguenti:

  • Matrici di lingue:

    float arr[] = {0.4f, 12.5f, 16.234f};
    
    for(auto val: arr)
    {
        std::cout << val << " ";
    }
    

    Si noti che l'assegnazione di un array dinamico non conta:

    float *arr = new float[3]{0.4f, 12.5f, 16.234f};
    
    for(auto val: arr) //Compile error.
    {
        std::cout << val << " ";
    }
    
  • Qualsiasi tipo che ha funzioni membro begin() e end() , che restituiscono gli iteratori agli elementi del tipo. I contenitori di libreria standard sono idonei, ma è possibile utilizzare anche i tipi definiti dall'utente:

    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 << " ";
        }
    }
    
  • Qualsiasi tipo che ha funzioni di begin(type) e end(type) non membro che possono essere trovate tramite ricerca dipendente dall'argomento, in base al type . Questo è utile per creare un tipo di intervallo senza dover modificare il tipo di classe stesso:

    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 << " ";
        }
    }