222 votes

Capture Lambda comme référence constante?

Est-il possible de capturer par référence const dans une expression lambda?

Je veux que la tâche indiquée ci-dessous échoue, par exemple:

 #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;
}
 

142voto

Steve M Points 4137

const n'est pas dans la grammaire pour les captures à partir de n3092:

 capture:
  identifier
  & identifier
  this
 

Le texte ne mentionne que capture par copie et capture par référence et ne mentionne aucune sorte de const-ness.

Cela me semble un oubli, mais je n'ai pas suivi le processus de normalisation de très près.

16voto

zhb Points 36

Je pense que la partie capture ne doit pas spécifier const , car la capture signifie, elle a seulement besoin d’un moyen d’accéder à la variable de portée externe.

Le spécificateur est mieux spécifié dans la portée externe.

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

La fonction lambda est const (la valeur ne peut pas changer dans sa portée). Ainsi, lorsque vous capturez une variable par valeur, la variable ne peut pas être modifiée, mais la référence ne se trouve pas dans la portée lambda.

9voto

Klaim Points 24511

Je suppose que si vous n'êtes pas à l'aide de la variable en paramètre de la functor, alors vous devriez utiliser le niveau d'accès de la fonction en cours. Si vous pensez que vous ne devriez pas, puis séparez vos lambda à partir de cette fonction, il n'est pas partie.

De toute façon, vous pouvez facilement obtenir la même chose que vous souhaitez en utilisant un autre const référence à la place :

#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;
}

Mais c'est le même qu'en supposant que votre lambda être isolé à partir de la fonction en cours, en faire un non-lambda.

5voto

Alex Points 3973

Je pense que vous avez trois options différentes:

  • ne pas utiliser const référence, mais l'utilisation d'une copie de la capture
  • ignorer le fait qu'il est modifiable
  • utiliser std::bind pour lier un argument d'une fonction binaire qui a const référence.

à l'aide d'une copie

La partie intéressante sur les lambdas avec copie capture, c'est que ceux qui sont effectivement en lecture seule et donc faire exactement ce que vous voulez.

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

en utilisant std::bind

std::bind réduit l'arité de la fonction. Notez cependant que cela pourrait/qui va conduire à une indirecte de l'appel de fonction par l'intermédiaire d'un pointeur de fonction.

int main() {
  int a = 5;
  std::function<int ()> f2 = std::bind( [](const int &a){return a;}, a);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X