Je voudrais être en mesure de déterminer au moment de la compilation, étant donné un type lambda générique, si elle peut être invoquée avec un ensemble donné de types de paramètres. J'ai l'exemple suivant d'implémentation C++ 14:Existe-t-il un moyen de détecter au moment de la compilation si un lambda générique peut être appelé avec succès avec un ensemble donné de types de paramètres?
#include <iostream>
// helper function; this overload handles the case that the call is possible
// use SFINAE with the extra template parameter to remove this from consideration when the
// call is ill-formed
template <typename Func, typename... Args, typename = decltype(std::declval<Func>()(std::declval<Args>()...))>
auto eval(Func f, Args &&... args) { return f(args...); }
// special type returned from `eval()` when the call can't be done
struct invalid_call { };
// helper function; this overload handles the case that the call is not possible
template <typename Func>
invalid_call eval(Func f, ...) { return invalid_call{}; };
// bring in std::negation from C++17 to help create the below trait
template<class B>
struct negation : std::integral_constant<bool, !bool(B::value)> { };
// trait that determines whether `Func` can be invoked with an argument list of types `Args...`
template <typename Func, typename... Args>
using can_call = negation<std::is_same<decltype(eval(std::declval<Func>(), std::declval<Args>()...)), invalid_call>>;
// arbitary type that has no `operator+`
struct foo {};
int main()
{
auto func = [](auto a1, auto a2) -> decltype(a1 + a2) { return a1 + a2; };
using FuncType = decltype(func);
std::cout << "can call with (int, int): " << can_call<FuncType, int, int>::value << std::endl;
std::cout << "can call with (foo, foo): " << can_call<FuncType, foo, foo>::value << std::endl;
}
Cet exemple fonctionne correctement tel quel. Ce que je ne suis pas LIKE est la façon lourde que le lambda doit être déclaré:
auto func = [](auto a1, auto a2) -> decltype(a1 + a2) { return a1 + a2; };
C'est, le type de retour de fuite doit être spécifiée parce que C++14's deduced return types don't work with SFINAE. La déduction de type de retour nécessite la substitution des types de liste d'arguments dans l'opérateur d'appel de modèle de l'appelable, et le programme est mal formé si une erreur s'y produit.
Idéalement, je serais en mesure de faire ce qui suit:
auto func = [](auto a1, auto a2) { return a1 + a2; };
et laisser le type de retour lui-même automatiquement; ce serait l'interface la plus intuitive à fournir à mes utilisateurs. Ceci est un exemple très simple, donc l'argument du decltype()
ne semble pas mauvais, mais en pratique, le lambda pourrait être plusieurs instructions, ce qui ne fonctionnerait pas avec cette approche. Donc ma question est:
En utilisant des techniques C++ modernes (C++ 14 serait le mieux, mais je suis ouvert aux nouvelles fonctionnalités si nécessaire), est-il possible de tester au moment de la compilation si un lambda générique peut éventuellement être appelé avec une liste arbitraire de types de paramètres?
On dirait que vous avez la réponse à votre question dans votre question: non, pas sans le type de retour-retour. – Barry
@Barry: Je pensais que cela pourrait être le cas, mais mes connaissances modernes en C++ ne font pas autorité, donc je voulais voir s'il y avait d'autres trucs là-bas. Si je suis limité par le besoin du type de retour, cela rend cette approche beaucoup moins utile puisque le lambda ne peut pas avoir plusieurs instructions. –
Étant donné que la déduction de type de retour nécessite que votre lambda déduise le même type de retour à chaque déduction, vous pouvez simplement utiliser la première déclaration de retour (ou la plus simple) comme type de retour final decltype. Ce n'est pas vraiment une solution, mais cela réduit l'espace du problème. – TBBle