initialized lambda capture – Italian C++ Community https://www.italiancpp.org Mon, 24 Aug 2020 13:03:53 +0000 it-IT hourly 1 https://wordpress.org/?v=4.7.18 106700034 Brace initialization inside a lambda capture list https://www.italiancpp.org/2014/05/26/brace-initialization-inside-a-lambda-capture-list/ Mon, 26 May 2014 13:24:05 +0000 http://www.italiancpp.org/?p=3229 Mi sono inmbattuto oggi in un problema abbastanza subdolo:

#include <iostream>
#include <functional>
#include <vector>
using namespace std;
function<void()> f()
{
    vector<int> v(1000, 0);
    cout << "v size is: " << v.size() << endl;

    return [ v {move(v)} ]() {
        cout << "inside lambda => v size is: " << v.size() << endl;
    };
}

int main()
{
    f() ();
}


Se eseguite il codice, l’output del programma è (ammetto con mia sorpresa) il seguente:

v size is: 1000
inside lambda => v size is: 1

La dimensione di v all’interno della lambda è 1. Ma perchè?
Il problema, come vedremo, non ha nulla a che vedere con le lambda in se ma risiede nel modo in cui v viene dedotto nella capture list.

Dato un vettore cosi definito:

vector<int> v(1000, 0); // crea 1000 interi inizializzati a 0


e altri due oggetti (t, u) cosi definiti:

vector<int> u{10,20,30};
auto t{10,20,30};

u è un vettore come tutti ce lo aspettiamo, completamente definito ed inizializzato (tramite la uniform initialization) con tre numeri (10, 20, 30), t invece è definito tramite auto. Il compilatore deduce t come std::initializer_list<decltype(v)> (= std::initializer_list<vector<int>>)!.

Ecco perchè v catturata dentro la lambda, definita implicitamente come auto, ha dimensione 1 (in quel caso abbiamo una std::initializer_list<vector<int>>). Per evitare il problema dobbiamo dunque riscrivere la nostra funzione in questo modo:

#include <iostream>
#include <functional>
#include <vector>
using namespace std;

function<void()> f()
{
    vector<int> v(1000, 0);
    cout << "v size is: " << v.size() << endl;

    return [ v = move(v) ]() { //adesso v è un vector<int>
        cout << "inside lambda => v size is: " << v.size() << endl;
    };
}

int main()
{
    f()();
}

Finalmente il compilatore dedurrà correttamente v nella capture list come un vector<int>. Con questa modifica il risultato è quello atteso!

Concludendo: evitate di usare la brace initialization all’interno delle capture list, ma piuttosto utilizzate la notazione VAR = EXPR, perchè auto + brace initialization implica che il tipo dedotto sia una std::initializer_list.

Biblio: [Scott Meyers] – Item 7 More Effective C++

]]>
3229