funciones examples c++ c++11 lambda c++14 initializer-list

examples - std c++



¿Por qué no puedo devolver la lista de inicializador de lambda (3)

¿Por qué este código no es válido?

auto foo=[](){ return {1,2}; };

Sin embargo, esto es válido ya que la initializer list se usa solo para inicializar un vector para no devolverse a sí mismo:

auto foo=[]()->std::vector<int>{ return {1,2}; };

¿Por qué no puedo devolver la initializer list ? Podría ser útil. Por ejemplo, un lambda que se puede usar para inicializar un vector o una list o ... con algunos valores predeterminados para algo.


La deducción del tipo de retorno Lambda usa las reglas auto , que normalmente hubieran deducido std::initializer_list muy bien. Sin embargo, los diseñadores de idiomas prohibieron la deducción de una lista de inicializadores en una declaración de devolución ([dcl.spec.auto] / 7):

Si la deducción es por una declaración de return y el inicializador es una lista iniciada con refuerzos ([dcl.init.list]), el programa está mal formado.

La razón de esto es que std::initializer_list tiene semántica de referencia ( eel.is/c++draft/dcl.init.list#6 ).
[]() -> std::initializer_list<int> { return {1, 2}; } []() -> std::initializer_list<int> { return {1, 2}; } es tan malo como
[]() -> const int & { return 1; } []() -> const int & { return 1; } . La vida útil de la matriz de respaldo del objeto initializer_list finaliza cuando la lambda regresa, y queda con un puntero colgante (o dos).

Demo :

#include <vector> struct Noisy { Noisy() { __builtin_printf("%s/n", __PRETTY_FUNCTION__); } Noisy(const Noisy&) { __builtin_printf("%s/n", __PRETTY_FUNCTION__); } ~Noisy() { __builtin_printf("%s/n", __PRETTY_FUNCTION__); } }; int main() { auto foo = []() -> std::initializer_list<Noisy> { return {Noisy{}, Noisy{}}; }; std::vector<Noisy> bar{foo()}; }

Salida:

Noisy::Noisy() Noisy::Noisy() Noisy::~Noisy() Noisy::~Noisy() Noisy::Noisy(const Noisy&) Noisy::Noisy(const Noisy&) Noisy::~Noisy() Noisy::~Noisy()

Observe cómo se llama a los constructores de copia después de que todos los objetos Noisy creados hasta ahora ya hayan sido destruidos.


Puede devolver un initializer_list de la función de la siguiente manera:

return std::initializer_list<int>{1, 2};

o

auto ret = {1, 2}; return ret;

El motivo es que la declaración de variable auto utiliza reglas diferentes a la deducción del tipo de devolución auto . El primero tiene una regla especial para este caso, y el segundo usa una deducción de tipo de plantilla simple.

Esto se discute en detalle en Scott Meyers Effective Modern C ++, Item 2. También hay un video y slides de él sobre el tema.


std::initializer_list no puede ser deducido por un argumento de plantilla, lo que significa que tendrá que decirle al lambda lo que es explícitamente:

#include <initializer_list> #include <iostream> #include <vector> int main() { auto foo = []() -> std::initializer_list<int> { return {1, 2}; }; std::vector<int> bar{foo()}; for (int x : bar) { std::cout << x << " "; }; }

Demo Aquí está la razón detrás de esto de la propuesta de lista de inicializador :

¿Se puede usar una lista de inicializadores como un argumento de plantilla? Considerar:

template<class T> void f(const T&); f({ }); // error f({1}); f({1,2,3,4,5,6}); f({1,2.0}); // error f(X{1,2.0}); // ok: T is X

Obviamente, no hay ningún problema con la última llamada (siempre que X {1,2.0} sea ​​válido)
porque el argumento de la plantilla es una X Dado que no estamos introduciendo listas arbitrarias de
tipos (tipos de productos), no podemos deducir que T sea {int, double} para f ({1,2.0}) , por lo que esa llamada es
un error. Plain {} no tiene un tipo, por lo que f ({}) también es un error.

Esto deja las listas homogéneas. ¿Deberían aceptarse f ({1}) y f ({1,2,3,4,5,6}) ? Si es así,
¿Con qué significado? Si es así, la respuesta debe ser que el tipo deducido, T , es
initializer_list . A menos que a alguien se le ocurra al menos un buen uso de este sencillo
característica (se deduce que una lista homogénea de elementos de tipo E es una
initializer_list ), no lo propondremos y todos los ejemplos serán errores: Sin plantilla
el argumento se puede deducir de una lista de inicializadores (no calificada). Una razón para ser cauteloso
Aquí es donde podemos imaginar a alguien confundiéndose sobre las posibles interpretaciones.
de listas de un solo elemento. Por ejemplo, ¿podría f ({1}) invocar f <int> (1) ? No, eso sería
bastante inconsistente