L'appel à lambda est ambigu malgré l'indication explicite du type de retour

11

Une fonction surchargée devrait prendre les deux foncteurs, étant donné que le type du lambda est décidable (transtypable en un std::function(veuillez me corriger si je me trompe). La question est: pourquoi y a-t-il une erreur de compilation ci-dessous, alors que le type lambda est explicitement défini? ( [&]() -> Type {})

Veuillez noter que pour ma solution actuelle, j'ai besoin de la capture par référence, c'est pourquoi le code contient la logique pour cela.

L'exemple suivant décrit le problème:

#include <iostream>
#include <string>    
#include <functional>

void do_some(std::function<void(int)> thing) 
{
   thing(5);
}

void do_some(std::function<bool(int)> thing)
{
   if (thing(10)) 
   {
      std::cout << "it's true!" << std::endl;
   }
}

int main()
{
   int local_to_be_modified = 0;
   do_some(
      [&](int in)
      {
         local_to_be_modified = in;
         std::cout << "This is void-" << std::endl;
      }
   );
   do_some(
      [&](int in) -> bool
      { 
         // error: call to 'do_some' is ambiguous
         local_to_be_modified += in;
         std::cout << "This is bool-" << std::endl;
         return true;
      }
   );
}
David Tóth
la source
6
Parce que std::function<void(int)>peut être construit même à partir d'un lambda qui renvoie quelque chose (ce qui entraîne l'ignorance de la valeur de retour).
HolyBlackCat
1
En passant, spécifier explicitement le type de retour de ce lambda ne fait rien.
Déduplicateur

Réponses:

8

Parce que la 2ème expression lambda de retour boolpourrait se convertir à la fois std::function<void(int)>et std::function<bool(int)>implicitement.

std::function a un constructeur de conversion:

template< class F >
function( F f );

Ce constructeur ne participe pas à la résolution de surcharge sauf si f est Callable pour les types d'arguments Args ... et retourne le type R. (depuis C ++ 14)

Comme la définition de Callable ,

Les expressions suivantes doivent être valides:

INVOKE<R>(f, std::declval<ArgTypes>()...)

INVOKE (f, t1, t2, ..., tN) est défini comme static_cast<void>(INVOKE(f, t1, t2, ..., tN))si R est éventuellement qualifié cv void, sinon INVOKE (f, t1, t2, ..., tN) , implicitement converti en R

Notez que le 2ème lambda retournant bool, pour le std::function<void(int)>, comme indiqué ci-dessus, static_cast<void>(INVOKE(f, t1, t2, ..., tN))est une expression valide (le retour boolest juste converti en void). Ensuite, il pourrait également se convertir en std::function<void(int)>implicitement et provoquer le problème d'ambiguïté.

songyuanyao
la source
6

Vous pouvez explicitement static_castle lambda au type approprié

using FunBoolRet = std::function<bool(int)>;

do_some(static_cast<FunBoolRet >([&](int in) 
   {
      local_to_be_modified += in;
      std::cout << "This is bool-" << std::endl;
      return true;
   }));

Ou stockez le lambda dans le std::function<bool(int)>type approprié et passez à la fonction (si elle do_some(lmda)doit être appelée plusieurs fois)

FunBoolRet lmda = [&](int in)
{
    local_to_be_modified += in;
    std::cout << "This is bool-" << std::endl;
    return true;
};    
do_some(lmda); // pass the lambda

Ou comme @MaxLanghof l'a suggéré, il suffit de construire à std::function<bool(int)>partir de lambda en déplacement

do_some(FunBoolRet{
   [&](int in) 
   {
      local_to_be_modified += in;
      std::cout << "This is bool-" << std::endl;
      return true;
   }
});
JeJo
la source
Vous pouvez ignorer le static_castet juste en construire un std::functiondirectement. C'est tout ce qui se passe pendant la conversion implicite de toute façon.
Max Langhof
Mon point est que vous pouvez littéralement simplement supprimer le static_cast<et dernier >et il fera la même chose mais avec moins de frappe. Il n'a pas besoin de plus de lignes ou de quoi que ce soit. godbolt.org/z/fQTqF4
Max Langhof