O compilador pode compilar run
a função sem falhas. Mas eu não sei como chamar essa função.
#include <iostream>
#include <functional>
#include <utility>
#include <string>
template<class... Args>
void run(std::function<void(Args...)>&& f, Args&&... args) {
f(std::forward<Args>(args)...);
}
void greet(std::string name, int times) {
for(int i = 0; i < times; ++i) {
std::cout << "Hello, " << name << "!\n";
}
}
int main() {
std::function<void(std::string, int)> func = greet;
run<std::string, int>(greet, std::string("Bob"), 2); // Not working
run(func, std::string("Bob"), 2); // Not working
return 0;
}
O código a seguir está ok e funciona perfeitamente. Só não consegui descobrir como a função no código acima poderia ser chamada.
template<class Functor, typename... Args>
void run(Functor&& f, Args&&... args) {
f(std::forward<Args>(args)...);
}
A fonte da falha na dedução são dois descuidos comuns:
Uma referência rvalue de argumento com tipo que não faz parte da lista de argumentos tempate. Argumento
f
aqui NÃO é uma referência de encaminhamento, é uma referência rvalue.Se você remover esse &&, esse caso de uso específico será compilado:
Outro é que o argumento da função tem o tipo dependendo do pacote de argumentos e por isso o outro caso não será compilado, pois o valor da expressão
greet
é um ponteiro, nãostd::function
. Pode serSe a função
greet
aceitar parâmetros de referência, isso resultará em dedução inconsistente do pacote de parâmetros, por exemplo:causaria falha no código por causa da correspondência de referência de rvalue para lvalue, o que exigiria inúmeras soluções alternativas. Pacotes de parâmetros e argumentos que dependem deles exigem correspondência exata.
Há boas razões pelas quais
std::evoke
uma versão mais geral dorun()
que foi projetado foi:Isso não é apenas livre de incompatibilidade de tipos, mas também permite evitar o despacho de chamadas em tempo de execução incorporado em
std::function
. Se isso não for aceitável ou o despacho em tempo de execução for necessário, é melhor usar os padrões clássicos Visitor, Callback ou Command em vez de tentar usar wrappers de ponteiros de função.Um erro comum é tentar usar
std::function
em templates. Se isso for usado, então seus parâmetros de template não estão em contexto de dedução. Se você olhar os templates da biblioteca padrão, verá que eles usamCallable
o parâmetro template. Use esse parâmetro também.https://godbolt.org/z/aMcbre7af