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