La llamada a lambda es ambigua a pesar de indicar explícitamente el tipo de retorno

11

Una función sobrecargada debería incluir a ambos functores, dado que el tipo de lambda es decidible (se puede convertir en un std::function(corríjame si me equivoco). La pregunta es: ¿Por qué hay un error de compilación a continuación, a pesar de que el tipo de lambda es explícitamente? definido? ( [&]() -> Type {})

Tenga en cuenta que para mi solución actual necesito la captura por referencia, es por eso que el código contiene la lógica para ello.

El siguiente ejemplo describe el problema:

#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
fuente
66
Porque std::function<void(int)>se puede construir incluso a partir de una lambda que devuelve algo (lo que hace que se ignore el valor de retorno).
HolyBlackCat
1
Como comentario, especificar explícitamente el tipo de retorno de esa lambda no hace exactamente nada.
Deduplicador

Respuestas:

8

Porque la segunda expresión lambda que regresa boolpodría convertirse en ambos std::function<void(int)>e std::function<bool(int)>implícitamente.

std::function tiene un constructor de conversión:

template< class F >
function( F f );

Este constructor no participa en la resolución de sobrecarga a menos que f sea invocable para los tipos de argumento Args ... y devuelva el tipo R. (desde C ++ 14)

Como la definición de invocable ,

Las siguientes expresiones deben ser válidas:

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

donde INVOKE (f, t1, t2, ..., tN) se define como static_cast<void>(INVOKE(f, t1, t2, ..., tN))si R es posiblemente calificado por cv void; de lo contrario, INVOKE (f, t1, t2, ..., tN) , se convierte implícitamente en R

Tenga en cuenta que la segunda lambda que regresa bool, ya que std::function<void(int)>, como se muestra arriba, static_cast<void>(INVOKE(f, t1, t2, ..., tN))es una expresión válida (la devuelta boolse acaba de convertir a void). Entonces también podría convertirse std::function<void(int)>implícitamente y causar el problema de ambigüedad.

songyuanyao
fuente
6

Puede explícitamente static_castla lambda al tipo apropiado

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

O almacene el lambda en el std::function<bool(int)>tipo apropiado y pase a la función (si se do_some(lmda)debe llamar muchas veces)

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

O como sugirió @MaxLanghof, simplemente construya std::function<bool(int)>desde lambda sobre la marcha

do_some(FunBoolRet{
   [&](int in) 
   {
      local_to_be_modified += in;
      std::cout << "This is bool-" << std::endl;
      return true;
   }
});
JeJo
fuente
Puede omitir el static_casty simplemente construir un std::functiondirectamente desde él. Eso es todo lo que sucede durante la conversión implícita de todos modos.
Max Langhof
Mi punto es que, literalmente, puedes eliminar el static_cast<último >y último y hará lo mismo pero con menos tipeo. No necesita más líneas ni nada. godbolt.org/z/fQTqF4
Max Langhof