A chamada para lambda é ambígua, apesar de declarar explicitamente o tipo de retorno

11

Uma função sobrecarregada deve receber os dois functores, dado que o tipo de lambda é decidível (pode ser convertido em um std::function(por favor, me corrija se estiver errado) .A pergunta é: por que há um erro de compilação abaixo, apesar de o tipo lambda ser explicitamente definido? ( [&]() -> Type {})

Observe que, para a minha solução atual, preciso da captura por referência; é por isso que o código contém a lógica para isso.

O exemplo a seguir descreve o 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
fonte
6
Porque std::function<void(int)>pode ser construído mesmo a partir de um lambda que retorna algo (que faz com que o valor de retorno seja ignorado).
30919 HolyBlackCat
11
Como um aparte, especificar explicitamente o tipo de retorno desse lambda não faz exatamente nada.
Deduplicator

Respostas:

8

Como a segunda expressão lambda retornada boolpode ser convertida em ambos std::function<void(int)>e std::function<bool(int)>implicitamente.

std::function possui um construtor de conversão:

template< class F >
function( F f );

Esse construtor não participa da resolução de sobrecarga, a menos que f seja Callable para os tipos de argumento Args ... e retorne o tipo R. (desde C ++ 14)

Como a definição de Callable ,

As seguintes expressões devem ser válidas:

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

onde INVOKE (f, t1, t2, ..., tN) é definido como static_cast<void>(INVOKE(f, t1, t2, ..., tN))se R é possivelmente qualificado como cv void, caso contrário INVOKE (f, t1, t2, ..., tN) , implicitamente convertido em R

Observe que o segundo retorno lambda bool, para o std::function<void(int)>, como mostrado acima, static_cast<void>(INVOKE(f, t1, t2, ..., tN))é uma expressão válida (o retorno boolé apenas convertido em void). Em seguida, também pode converter std::function<void(int)>implicitamente e causa o problema de ambiguidade.

songyuanyao
fonte
6

Você pode explicitamente static_casto lambda para o tipo apropriado

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 armazene o lambda no std::function<bool(int)>tipo apropriado e passe para a função (se do_some(lmda)for chamado várias vezes)

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 como o @MaxLanghof sugeriu, simplesmente construa a std::function<bool(int)>partir do lambda em movimento

do_some(FunBoolRet{
   [&](int in) 
   {
      local_to_be_modified += in;
      std::cout << "This is bool-" << std::endl;
      return true;
   }
});
JeJo
fonte
Você pode pular o static_caste apenas construir um std::functiondiretamente a partir dele. É tudo o que acontece durante a conversão implícita.
precisa
Meu argumento é que você pode literalmente remover o static_cast<último >e ele fará a mesma coisa, mas com menos digitação. Não precisa de mais linhas nem nada. godbolt.org/z/fQTqF4
Max Langhof