web-dev-qa-db-fra.com

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

Mise à jour: Comme il s’agit d’une question ancienne, il serait bon de la mettre à jour s’il existe des installations en C++ 14 pour vous aider. Les extensions de C++ 14 nous permettent-elles de capturer un objet non-const par référence const? ( août 2015 )

135
John Dibling

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.

C'est comme un oubli, mais je n'ai pas suivi le processus de normalisation de très près.

102
Steve M

C++ 14:

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

D&EACUTE;MO


C++ 17:

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

DEMO 2

70
Piotr Skotnicki

Je pense que la partie capture ne doit pas spécifier const car, en tant que moyen de capture, elle nécessite uniquement 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.
}

Fonction lambdaest const (ne peut pas modifier la valeur dans son étendue). Ainsi, lorsque vous capturez variable par valeur, la variable ne peut pas être modifiée, mais la référence n'est pas dans l'étendue lambda. 

12
zhb

Je suppose que si vous n'utilisez pas la variable en tant que paramètre du foncteur, vous devez alors utiliser le niveau d'accès de la fonction actuelle. Si vous pensez que vous ne devriez pas, alors séparez votre lambda de cette fonction, il n'en fait pas partie.

Quoi qu'il en soit, vous pouvez facilement obtenir la même chose que vous voulez en utilisant une autre référence const: 

#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 cela revient à supposer que votre lambda doit être isolé de la fonction actuelle, ce qui en fait un non-lambda.

8
Klaim

Je pense que vous avez trois options différentes:

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

en utilisant une copie

La partie intéressante à propos des lambdas avec capture de copie est que ceux-ci sont en réalité uniquement en lecture et font donc 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é d'une fonction. Notez cependant que cela pourrait entraîner un appel de fonction indirect via un pointeur de fonction.

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

Il y a un moyen plus court.

Notez qu'il n'y a pas de perluète avant "best_string".

Ce sera du type "const std :: reference_wrapper << T >>".

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

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

3
Sergey Palitsin

En utilisant un const, l'algorithme et commercial définiront simplement la chaîne à sa valeur initiale, .__ En d'autres termes, le lambda ne se définira pas vraiment en tant que paramètre de la fonction, bien que la portée environnante ait une variable supplémentaire ... Sans le définir cependant, il ne définirait pas la chaîne comme étant le type [&, & best_string] (string const s) Par conséquent, il est probablement préférable de le laisser si nous en restons là, en essayant de capturer la référence.

0
Saith

Utilisez clang ou attendez que ce bogue gcc soit corrigé: Bogue 70385: la capture lambda par référence à une référence const échoue [ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70385 ]

0
user1448926