Captura Lambda como referencia const?


¿Es posible capturar por referencia const en una expresión lambda?

Quiero que la tarea marcada a continuación falle, por ejemplo:

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";

    for_each( &strings[0], &strings[num_strings], [&best_string](const string& s)
      {
        best_string = s; // this should fail
      }
    );
    return 0;
}

Update: Como esta es una pregunta antigua, podría ser bueno actualizarla si hay facilidades en C++14 para ayudar con esto. ¿Las extensiones en C++14 nos permiten capturar un objeto no-const por referencia const? (Agosto 2015)

Author: Aaron McDaid, 2010-09-22

8 answers

const no está en la gramática para capturas a partir de n3092:

capture:
  identifier
  & identifier
  this

El texto solo menciona captura por copia y captura por referencia y no menciona ningún tipo de constancia.

Se siente como un descuido para mí, pero no he seguido el proceso de estandarización muy de cerca.

 96
Author: Steve M,
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
2010-09-22 19:40:16

C++14:

[&best_string = static_cast<const std::string&>(best_string)](const string& s)
{
    best_string = s; // fails
};

DEMO


C++17:

[&best_string = std::as_const(best_string)](const string& s)
{
    best_string = s; // fails
};

DEMO 2

 55
Author: Piotr Skotnicki,
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
2018-06-10 07:31:31

Creo que la parte de captura no debe especificar const, ya que la captura significa que solo necesita una forma de acceder a la variable de alcance externo.

El especificador se especifica mejor en el ámbito exterior.

const string better_string = "XXX";
[&better_string](string s) {
    better_string = s;    // error: read-only area.
}

función lambda es const (no se puede cambiar el valor en su ámbito), por lo que cuando captura variable por valor, la variable no se puede cambiar, pero la referencia no está en el ámbito lambda.

 12
Author: zhb,
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
2014-01-04 03:45:10

Supongo que si no estás usando la variable como parámetro del funtor, entonces deberías usar el nivel de acceso de la función actual. Si crees que no deberías, entonces separa tu lambda de esta función, no es parte de ella.

De todos modos, puede lograr fácilmente lo mismo que desea utilizando otra referencia const en su lugar:

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";
    const string& string_processed = best_string;

    for_each( &strings[0], &strings[num_strings], [&string_processed]  (const string& s)  -> void 
    {
        string_processed = s;    // this should fail
    }
    );
    return 0;
}

Pero eso es lo mismo que asumir que tu lambda tiene que estar aislada de la función actual, convirtiéndola en una no-lambda.

 8
Author: Klaim,
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
2011-11-18 14:01:36

Creo que tienes tres opciones diferentes:

  • no use la referencia const, pero use una captura de copia
  • ignorar el hecho de que es modificable
  • use std::bind para enlazar un argumento de una función binaria que tiene una referencia const.

Usando una copia

La parte interesante de las lambdas con copy captures es que en realidad son de solo lectura y, por lo tanto, hacen exactamente lo que quieres que hagan.

int main() {
  int a = 5;
  [a](){ a = 7; }(); // Compiler error!
}

Usando std:: bind

std::bind reduce la aridad de una función. Sin embargo, tenga en cuenta que esto podría/conducirá a una llamada indirecta a la función a través de un puntero de función.

int main() {
  int a = 5;
  std::function<int ()> f2 = std::bind( [](const int &a){return a;}, a);
}
 5
Author: Alex,
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
2013-12-11 20:19:37

Hay un camino más corto.

Tenga en cuenta que no hay ampersand antes de "best_string".

Será de tipo "const std::reference_wrapper>".

[best_string = cref(best_string)](const string& s)
{
    best_string = s; // fails
};

Http://coliru.stacked-crooked.com/a/0e54d6f9441e6867

 3
Author: Sergey Palitsin,
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
2017-09-13 16:52:28

Use clang o espere hasta que se solucione este error de gcc: error 70385: Falla la captura Lambda por referencia de la referencia const [ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70385 ]

 0
Author: user1448926,
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
2016-12-02 17:55:38

El uso de una const simplemente hará que el algoritmo ampersand establezca la cadena en su valor original, En otras palabras, la lambda realmente no se definirá como parámetro de la función, aunque el ámbito circundante tendrá una variable adicional... Sin embargo, sin definirlo, no definiría la cadena como la típica [ & , & best_string](string const s) Por lo tanto, es más probable que sea mejor si lo dejamos así, tratando de capturar la referencia.

 0
Author: Saith,
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
2018-02-02 20:24:32