Eu sei que o código a seguir não será compilado.
void baz(int i) { }
void baz() { }
class Bar
{
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = baz) : bazFn(fun){}
};
int main(int argc, char **argv)
{
Bar b;
return 0;
}
Porque std::function
é dito para não considerar a resolução de sobrecarga, como eu li em neste outro post .
Não compreendo completamente as limitações técnicas que forçaram esse tipo de solução.
Eu li sobre as fases da tradução e dos modelos na cppreference, mas não consigo pensar em nenhum raciocínio para o qual não encontrei um contra-exemplo. Explicado a um meio-leigo (ainda novo no C ++), o que e durante qual estágio da tradução faz com que o acima falhe na compilação?
c++
templates
translation
std-function
TuRtoise
fonte
fonte
Respostas:
Isso realmente não tem nada a ver com "fases da tradução". É puramente sobre os construtores de
std::function
.Veja,
std::function<R(Args)>
não requer que a função fornecida seja exatamente do tipoR(Args)
. Em particular, não requer que seja dado um ponteiro de função. Pode levar qualquer tipo de chamada (ponteiro de função de membro, algum objeto que tenha uma sobrecarga deoperator()
), desde que seja invocável como se tivesseArgs
parâmetros e retornasse algo conversível paraR
(ou seR
forvoid
, pode retornar qualquer coisa).Para fazer isso, o construtor apropriado de
std::function
deve ser um modelo :template<typename F> function(F f);
. Ou seja, ele pode aceitar qualquer tipo de função (sujeito às restrições acima).A expressão
baz
representa um conjunto de sobrecargas. Se você usar essa expressão para chamar o conjunto de sobrecarga, tudo bem. Se você usar essa expressão como parâmetro para uma função que usa um ponteiro de função específico, o C ++ poderá reduzir a sobrecarga definida em uma única chamada, tornando-a adequada.No entanto, quando uma função é um modelo e você está usando a dedução de argumento de modelo para descobrir qual é esse parâmetro, o C ++ não tem mais a capacidade de determinar qual é a sobrecarga correta no conjunto de sobrecargas. Então você deve especificá-lo diretamente.
fonte
function
classe é irrelevante . O que importa é o parâmetro do modelo no construtor que você está chamando. Qual é apenastypename F
: aka, qualquer tipo.A resolução de sobrecarga ocorre apenas quando (a) você está chamando o nome de uma função / operador ou (b) está convertendo-o em um ponteiro (para função ou função membro) com uma assinatura explícita.
Nem está ocorrendo aqui.
std::function
pega qualquer objeto que seja compatível com sua assinatura. Não é necessário um ponteiro de função especificamente. (uma lambda não é uma função std e uma função std não é uma lambda)Agora, nas minhas variantes de função de homebrew, para assinatura
R(Args...)
, também aceito umR(*)(Args...)
argumento (uma correspondência exata) exatamente por esse motivo. Mas isso significa que eleva as assinaturas de "correspondência exata" acima das assinaturas "compatíveis".O principal problema é que um conjunto de sobrecarga não é um objeto C ++. Você pode nomear um conjunto de sobrecarga, mas não pode transmiti-lo "nativamente".
Agora, você pode criar um conjunto de pseudo-sobrecarga de uma função como esta:
isso cria um único objeto C ++ que pode sobrecarregar a resolução em um nome de função.
Expandindo as macros, obtemos:
o que é chato escrever. Uma versão mais simples, apenas um pouco menos útil, está aqui:
temos uma lambda que aceita qualquer número de argumentos e, em seguida, os encaminha para a perfeição
baz
.Então:
trabalho. Adiamos a resolução de sobrecarga para o lambda em que armazenamos
fun
, em vez de passarfun
diretamente um conjunto de sobrecargas (que não pode ser resolvido).Houve pelo menos uma proposta para definir uma operação na linguagem C ++ que converte um nome de função em um objeto de conjunto de sobrecarga. Até que uma proposta desse padrão esteja no padrão, a
OVERLOADS_OF
macro é útil.Você poderia dar um passo adiante e oferecer suporte ao ponteiro de função de conversão para compatível.
mas isso está começando a ficar obtuso.
Exemplo ao vivo .
fonte
A questão aqui é que não há nada dizendo ao compilador como executar a função de deterioração do ponteiro. Se você tem
Então o código funcionaria, já que agora o compilador sabe qual função você deseja, pois existe um tipo concreto ao qual você está atribuindo.
Quando você usa,
std::function
você chama o construtor de objetos de função que tem a formae como é um modelo, ele precisa deduzir o tipo do objeto que é passado. Como
baz
é uma função sobrecarregada, não há um tipo único que possa ser deduzido, portanto a dedução do modelo falha e você recebe um erro. Você teria que usarpara forçar um único tipo e permitir a dedução.
fonte
No momento, o compilador está decidindo qual sobrecarga deve passar para o
std::function
construtor. Tudo o que sabe é que ostd::function
construtor é modelado para usar qualquer tipo. Ele não tem a capacidade de tentar as duas sobrecargas e descobrir que o primeiro não compila, mas o segundo.A maneira de resolver isso é informar explicitamente ao compilador qual sobrecarga você deseja com um
static_cast
:fonte