J'ai une fonction qui prend deux std::function
s en arguments. Le paramètre de la deuxième fonction a le même type que le résultat de la première.
J'ai écrit un modèle de fonction comme ceci:
template<typename ResultType>
void examplFunction(std::function<ResultType()> func, std::function<void(ResultType)> func2) {
auto x = func();
func2(x);
}
Je peux l'appeler avec:
void f() {
examplFunction<int>([]() { return 1; }, //
[](int v) { std::cout << "result is " << v << std::endl; });
}
Existe-t-il un moyen de supprimer le <int>
à examplFunction<int>
et de laisser le compilateur déduire le type de ResultType
?
Avez-vous réellement besoin de std::function
dedans? std::function
est utile lorsque vous avez besoin de type erasure . Avec les modèles, vous pouvez généralement le sauter complètement:
template<class F1, class F2>
void examplFunction(F1 func, F2 func2, decltype(func2(func()))* sfinae = nullptr) {
auto x = func();
func2(x);
}
Le paramètre sfinae
permet de s'assurer que la fonction ne peut être appelée qu'avec des fonctions telles que func2
puisse être appelé avec le résultat de func
.
Oui il y a.
template<typename ResultType>
void examplFunction_impl(std::function<ResultType()> func, std::function<void(ResultType)> func2) {
auto x = func();
func2(x);
}
template<class F1, class F2>
void examplFunction(F1&& f1, F2&& f2)
{
using ResultType = decltype(f1());
examplFunction_impl<ResultType>(std::forward<F1>(f1), std::forward<F2>(f2));
}
Dans ce cas, vous avez besoin que f1
soit invocable sans arguments, de sorte que vous puissiez déterminer le type de retour dans la fonction d'assistance. Ensuite, vous appelez la fonction réelle en spécifiant explicitement ce type de retour.
Vous pouvez ajouter un peu de SFINAE pour vous assurer que cette fonction ne participe à la résolution de surcharge que lorsque f1
peut effectivement être appelé ainsi (et si f2
peut également être appelé avec la valeur de retour f1
).
Bien que je sois d’accord avec @Angew sur le fait que dans l’exemple donné, il n’est pas nécessaire d'utiliser std::function
. Cela pourrait bien sûr être différent dans une situation réelle.
std::function
a un constructeur basé sur un modèle (et par ailleurs non contraint), donc le déduire d'un simple type d'argument n'est pas une transaction aussi simple. Si ces arguments doivent toujours être std::function
s, vous pouvez ignorer un <int>
pour le prix de deux std::function
s et laisser les guides de déduction faire le reste:
void f() {
examplFunction(std::function([]() { return 1; }), //
std::function([](int v) { std::cout << "result is "
<< v << std::endl; }));
}
Un fait amusant est que cela ne fonctionne pas toujours. Par exemple, l'implémentation actuelle de libc ++ manque de guides pour std::function
, violant ainsi le standard.
La réponse d'Angew est excellente (et devrait être la réponse acceptée), mais il manque le détail mineur de la vérification que la fonction de section ne renvoie rien. Pour ce faire, vous devez utiliser le trait de type std::is_void
et std :: enable_if:
template<class F1, class F2>
void examplFunction(F1 func, F2 func2, std::enable_if_t<std::is_void_v<decltype(func2(func()))>, void*> sfinae = nullptr) {
auto x = func();
func2(x);
}
Cette évidence est plus verbeuse et difficile à lire si vous n'êtes pas familier avec les caractères de type et SFINAE. Ce n'est donc probablement pas la meilleure voie à suivre si vous n'avez pas besoin de vous assurer que F2 renvoie void
.