Type de retour explicite de Lambda

91

Lorsque j'essaye de compiler ce code (VS2010), j'obtiens l'erreur suivante: error C3499: a lambda that has been specified to have a void return type cannot return a value

void DataFile::removeComments()
{
  string::const_iterator start, end;
  boost::regex expression("^\\s?#");
  boost::match_results<std::string::const_iterator> what;
  boost::match_flag_type flags = boost::match_default;
  // Look for lines that either start with a hash (#)
  // or have nothing but white-space preceeding the hash symbol
  remove_if(rawLines.begin(), rawLines.end(), [&expression, &start, &end, &what, &flags](const string& line)
  {
    start = line.begin();
    end = line.end();
    bool temp = boost::regex_search(start, end, what, expression, flags);
    return temp;
  });
}

Comment ai-je spécifié que le lambda a un type de retour «void». De plus, comment spécifier que le lambda a un type de retour 'bool'?

METTRE À JOUR

Les compilations suivantes. Quelqu'un peut-il s'il vous plaît me dire pourquoi cela compile et l'autre pas?

void DataFile::removeComments()
{
  boost::regex expression("^(\\s+)?#");
  boost::match_results<std::string::const_iterator> what;
  boost::match_flag_type flags = boost::match_default;
  // Look for lines that either start with a hash (#)
  // or have nothing but white-space preceeding the hash symbol
  rawLines.erase(remove_if(rawLines.begin(), rawLines.end(), [&expression, &what, &flags](const string& line)
  { return boost::regex_search(line.begin(), line.end(), what, expression, flags); }));
}
Ryan
la source
6
Vous pouvez le spécifier explicitement avec ->, par exemple[&](double d) -> double { //...
Flexo
2
Je vous conseille de simplement capturer implicitement les variables dont vous avez besoin (uniquement [&]...), car ce que vous avez actuellement est inutilement verbeux.
Xeo
2
[&expression, &start, &end, &what, &flags]...(le vôtre) vs [&]...(le mien). Maintenant, dites-moi qui est le plus verbeux. ;) [&]dit au lambda de capturer tout ce que vous utilisez à l'intérieur du corps lambda, par référence. C'est ce qu'on appelle une "capture par défaut". L'autre est [=]et sera capturé par copie.
Xeo
1
@Xeo, Effective Modern C ++, Item 31, recommande de capturer explicitement, pour éviter les références en suspens. J'ai été mordu par ça plusieurs fois moi-même comme punition d'être paresseux ... euh, concis. :-)
Emile Cormier
2
En passant, les contraintes sont réduites sur les lambdas de type retour déduites en C ++ 14. Les types de retour peuvent être déduits pour les lambdas avec plus d'une instruction dans le corps, et tant que l'expression de chaque instruction de retour a le même type, vous pouvez maintenant avoir un type de retour déduit avec plusieurs instructions de retour.
Anthony Hall

Réponses:

188

Vous pouvez spécifier explicitement le type de retour d'un lambda en utilisant -> Typeaprès la liste d'arguments:

[]() -> Type { }

Cependant, si un lambda a une instruction et que cette instruction est une instruction de retour (et qu'elle renvoie une expression), le compilateur peut déduire le type de retour du type de cette expression retournée. Vous avez plusieurs instructions dans votre lambda, donc il ne déduit pas le type.

Seth Carnegie
la source
4
le compilateur peut le faire, mais la norme lui interdit de le faire.
Johannes Schaub - litb
9
-1: Ce n'est pas un bogue du compilateur. La norme est très claire à ce sujet: la section 5.1.2, paragraphe 4, décrit comment la déduction est effectuée et dans quelles conditions elle est.
Nicol Bolas
2
Bien que cela ne soit pas autorisé selon le dernier projet, je pourrais trouver qu'il semble que cela soit réellement autorisé dans les spécifications finales en passant par le commentaire de ce patch gcc.gnu.org/ml/gcc-patches/2011-08/msg01901.html . Quelqu'un a la spécification finale à vérifier?
Eelke
2
J'ai beaucoup utilisé les expressions lambda et je n'ai pas une seule fois énoncé explicitement le type de retour. La déduction du type de retour (au moins sous VS2012 et VS2013) fonctionne parfaitement même s'il y a plus d'une instruction de retour dans l'expression lambda. Bien sûr, les différentes instructions de retour doivent correspondre dans la même expression lambda. Par exemple, une instruction telle que "auto f = [] (int i) {if (i> 5) return true; return false;};" compile sans problème et si vous appelez "auto b = f (10);" b sera de type bool et bien sûr vrai;
sprite
1
return nullptr;peut jeter une clé dans la déduction de type, même si elle est valide quel que soit le type de pointeur renvoyé.
Grault
16

Le type de retour d'un lambda (en C ++ 11) peut être déduit, mais seulement lorsqu'il y a exactement une instruction, et cette instruction est une returninstruction qui renvoie une expression (une liste d'initialiseurs n'est pas une expression, par exemple). Si vous avez un lambda à plusieurs instructions, le type de retour est supposé être void.

Par conséquent, vous devez faire ceci:

  remove_if(rawLines.begin(), rawLines.end(), [&expression, &start, &end, &what, &flags](const string& line) -> bool
  {
    start = line.begin();
    end = line.end();
    bool temp = boost::regex_search(start, end, what, expression, flags);
    return temp;
  })

Mais vraiment, votre deuxième expression est beaucoup plus lisible.

Nicol Bolas
la source
Bel exemple; nitpick: Votre appel de fonction est-il manquant );à la fin?
kevinarpe
6

Vous pouvez avoir plus d'une instruction lorsque vous retournez toujours:

[]() -> your_type {return (
        your_statement,
        even_more_statement = just_add_comma,
        return_value);}

http://www.cplusplus.com/doc/tutorial/operators/#comma

Valen
la source
4
la virgule est un opérateur révoltant. cela déroute les gens qui ne sont pas conscients de son existence ou de son niveau de préséance. il n'y a jamais d'utilisation valable non plus. il peut toujours être évité avec plus de fonctions ou un code autrement mieux organisé.
jheriko
@jheriko d'accord, l'existence de ma réponse n'est que pour ceux qui veulent vraiment une solution XD indépendante à une seule ligne (c'est toujours une ligne, non?). La virgule n'est vraiment pas perceptible, et personne ne mettrait jamais toute la méthode principale sous cette forme.
Valen
1
Bien sûr, vous donnez certainement une réponse valable, je ne suis tout simplement pas fan de faire quoi que ce soit pour encourager ou même démontrer de mauvaises pratiques. une fois que les gens apprennent que la virgule est un opérateur, c'est un compte à rebours jusqu'à ce qu'ils commencent à en abuser et un compte à rebours plus long jusqu'à ce qu'ils apprennent mieux. :)
jheriko
@jheriko Je l'ai vu une fois utilisée de manière intéressante dans une liste d'initialisation de membres, mais c'était juste pour déconner, si je me souviens bien.
Justin Time - Réintègre Monica