c++ - compilador - El lambda genérico con std:: función no captura variables
gcc c++ (2)
Puedo reproducir esto a menos que haga cualquiera de los siguientes:
- eliminar
const
dea
- nombre
a
en la lista de captura - cambie
std::function<void(int)>
aauto
- hacer que la lambda no sea genérica cambiando
auto b
aint b
- use Clang (por ej. v3.5.0)
Creo que esto es un error del compilador relacionado con las optimizaciones y una falla al detectar odr-use en un lambda genérico (aunque es interesante que la configuración -O0
no tenga ningún efecto). Podría estar relacionado con el error 61814 pero no creo que sea lo mismo, por lo tanto:
Lo he planteado como error GCC 64791 .
- (Actualización: este error se marcó como corregido en GCC 5.0).
Ciertamente no puedo encontrar nada obvio en la redacción de C ++ 14 que debería denegar su código, aunque hay muy poco "obvio" en general en la nueva redacción de C ++ 14. :(
[C++14: 5.1.2/6]:
[..] Para un lambda genérico sin captura lambda, el tipo de cierre tiene una plantilla de función pública de conversión no virtual no virtual para señalar a la función. La plantilla de función de conversión tiene la misma plantilla-lista-lista inventada, y el puntero a función tiene los mismos tipos de parámetros, que la plantilla de operador de llamada a función. [..]
[C++14: 5.1.2/12]:
una expresión lambda con un valor predeterminado de captura asociado que no captura explícitamente esta o una variable con duración de almacenamiento automático (esto excluye cualquier expresión de id que se haya encontrado que haga referencia a un miembro de datos no estático asociado a la captura de inicio ), se dice que captura implícitamente la entidad (es decir,this
o una variable) si el enunciado compuesto :
- odr-usa (3.2) la entidad, o
- nombra la entidad en una expresión potencialmente evaluada (3.2) donde la expresión completa envolvente depende de un parámetro lambda genérico declarado dentro del alcance de alcance de la expresión lambda .
[Ejemplo:
void f(int, const int (&)[2] = {}) { } // #1 void f(const int&, const int (&)[1]) { } // #2 void test() { const int x = 17; auto g = [](auto a) { f(x); // OK: calls #1, does not capture x }; auto g2 = [=](auto a) { int selector[sizeof(a) == 1 ? 1 : 2]{}; f(x, selector); // OK: is a dependent expression, so captures x }; }
-ejemplo] Todas esas entidades capturadas implícitamente se declararán dentro del alcance de alcance de la expresión lambda. [Nota: la captura implícita de una entidad por una expresión lambda anidada puede causar su captura implícita por la expresión lambda que lo contiene (ver a continuación). Odr-usos implícitos de esto puede resultar en captura implícita. -finalizar nota]
[C++14: 5.1.2/13]:
una entidad se captura si se captura explícita o implícitamente. Una entidad capturada por una expresión lambda se usa odr (3.2) en el ámbito que contiene la expresión lambda . [..]
Estoy tratando de usar el lambda genérico de C ++ 14, pero tuve un problema con la función std ::.
#include <iostream>
#include <functional>
int main()
{
const int a = 2;
std::function<void(int)> f = [&](auto b) { std::cout << a << ", " << b << std::endl; };
f(3);
}
Esto no se puede compilar con un mensaje de error que dice que el error: ''a'' was not declared in this scope
.
Funciona si lo cambio a (int b)
.
¿Es un error? ¿O me estoy perdiendo algo?
La versión de GCC que estoy usando es 4.9.2.
int main() {
const int a = 2;
auto f = [&](auto b) { std::cout << a << ", " << b << std::endl; };
f(3);
}
No sé si debería funcionar con la std::function
pero esto funciona con seguridad.
Investigación exahustiva:
Creé una clase para imitar lo más fielmente posible la lambda:
class Functor {
private:
int const x;
public:
Functor() : x{24} {}
auto operator()(int b) const -> void { cout << x << " " << b << endl; }
};
std::function<auto(int)->void> f2 = Functor{};
f2(3); // <- this works
Esto sugiere que tu ejemplo debería haber funcionado. Después de todo, las lambdas tienen el mismo comportamiento con un objeto que tiene el operator()
sobrecargado y campos para las variables capturadas.
Si cambiamos la clase para llegar a la parte de auto
:
Esto no funciona:
class Functor {
private:
int const x;
public:
Functor() : x{24} {}
auto operator()(auto b) const -> void { cout << x << " " << b << endl; }
};
std::function<auto(int)->void> f2 = Functor{}; // <-- doesn''t work
Sin embargo, esto funciona:
class Functor {
private:
int const x;
public:
Functor() : x{24} {}
template <class T>
auto operator()(T b) const -> void { cout << x << " " << b << endl; }
};
std::function<auto(int)->void> f2 = Functor{}; // <-- this works
Por lo tanto, lo más probable es que esté relacionado con el uso de auto
como parámetro de lambda / funciones, una característica nueva en C ++ 14, probablemente sin una implementación madura.