Lambda genérico con función std::no captura variables


Estoy tratando de usar la lambda genérica de C++14, pero tengo 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 compila con un mensaje de error que dice que 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.

Author: Lightness Races in Orbit, 2015-01-25

2 answers

Puedo reproducir esto a menos que haga cualquiera de las siguientes cosas:{[14]]}

  • eliminar const de a
  • nombre a en la lista de captura
  • cambiar std::function<void(int)> a auto
  • haga que la lambda no sea genérica cambiando auto b a int b
  • use Clang (por ejemplo, v3.5.0)

Creo que este es un error del compilador relacionado con optimizaciones y un fallo al detectar odr-use en una lambda genérica (aunque es interesante que la configuración -O0 tiene sin efecto). Podría estar relacionado con bug 61814 pero no creo que sea bastante lo mismo, por lo tanto:

Lo he planteado como GCC bug 64791.

  • (Actualización: este error ha sido marcado como corregido en GCC 5.0.)

Ciertamente no puedo encontrar nada obvio en la redacción de C++14 que debería rechazar 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 una lambda genérica sin captura lambda, el tipo de cierre tiene una plantilla de función de conversión const pública no virtual no explícita a puntero a función. La plantilla de función de conversión tiene la misma plantilla inventada template-parameter-list, y el puntero a la función tiene los mismos tipos de parámetros, como la plantilla del operador de llamada a función. [..]

[C++14: 5.1.2/12]: A lambda-expresión con un asociado capture-default que no captura explícitamente esta o una variable con duración de almacenamiento automático (esto excluye cualquier id-expression que se ha encontrado que se refiere a un init-capture's asociado miembro de datos no estáticos), se dice que captura implícitamente la entidad (es decir, this o una variable) si la compound-statement :

  • odr-utiliza (3.2) la entidad, o
  • nombra la entidad en una expresión potencialmente evaluada (3.2) donde la expresión completa que encierra depende de un parámetro lambda genérico declarado dentro del 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
  };
}

-end example] Todas estas entidades capturadas implícitamente se declararán dentro del 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 la contiene (véase más adelante). Implicit odr-los usos de esto pueden resultar en una captura implícita. -nota final ]

[C++14: 5.1.2/13]: Una entidad es capturada si es capturada explícita o implícitamente. Una entidad capturada por una lambda-expression es odr-used (3.2) en el ámbito que contiene la lambda-expression. [..]

 22
Author: Lightness Races in Orbit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-03 16:19:02
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 std::function pero esto funciona con seguridad.

Investigación adicional:

He creado una clase para imitar lo más cerca 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 su ejemplo debería haber funcionado. Después de todo, las lambdas tienen el mismo comportamiento con un objeto que tiene los campos operator() sobrecargados y para las variables capturadas.

Si cambiamos la clase para llegar a la parte 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

Así que lo más probable es que esté relacionado con el uso de auto como parámetro de lambda/functions, una característica nueva en C++14, por lo que lo más probable es que no haya una implementación madura.

 3
Author: bolov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-25 21:20:14