J'ai une méthode qui prend un objet indexable comme paramètre de modèle, quelque chose comme:
template <typename OBJ> int foo(int n, OBJ o) { int x = 0; for (int i = 0; i < n; ++i) { x += o[i]; } return x; }
Existe-t-il un moyen de transmettre une fonction lambda pour le o
paramètre? En d'autres termes, faire en sorte que le lambda puisse être appelé via l'opérateur []
plutôt que l'opérateur ()
?
4 Réponses :
Vous pouvez le faire en:
opérateur []
est défini. operator []
en termes de operator ()
d'une std :: function
. std :: function
encapsulée en tant que variable membre du modèle de classe. Voici un programme de démonstration.
285
et sa sortie
#include <iostream> #include <functional> template <typename OBJ> int foo(int n, OBJ o) { int x = 0; for (int i = 0; i < n; ++i) { x += o[i]; } return x; } template <typename> struct Functor; template <typename R> struct Functor<R(int)> { using ftype = std::function<R(int)>; Functor(ftype f) : f_(f) {} R operator[](int i) const { return f_(i); } ftype f_; }; int main() { Functor<int(int)> f = {[](int i) -> int {return i*i;}}; std::cout << foo(10, f) << std::endl; }
Functor
n'est pas le bon nom ici. Il ne surcharge pas l'opérateur d'appel de fonction. Je soupçonne qu'il existe un nom plus approprié.
Je n'aime pas passer la signature.
Une esquisse d'un type de wrapper qui ferait cela.
#include <iostream> template <typename OBJ> int foo(int n, OBJ o) { int x = 0; for (int i = 0; i < n; ++i) { x += o[i]; } return x; } int main() { index_wrapper f([](int i) -> int { return i*i; }); std::cout << foo(10, f) << std::endl; }
Avec utilisation
template<typename UnaryFunction> class index_wrapper { public: index_wrapper(UnaryFunction func) : func(std::move(func)) {} template<typename T> std::invoke_result_t<UnaryFunction, T> operator[](T&& t) { return func(std::forward<T>(t)); } private: UnaryFunction func; };
Vous souhaiterez peut-être le limiter à un seul type de paramètre, afin de pouvoir fournir des alias de type de membre similaires à std :: vector :: reference
et.al.
Ceci est C ++ 17 - OP demandé 11
Eh bien, si cela vous aide, voici un moyen de transférer l ' opérateur []
d'une classe wrapper à l' opérateur ()
de votre lambda.
template<class F> struct SubscriptWrapper_t { F f_; template<class T> auto operator[](T const& t_) const -> decltype(f_(t_)) { return f_(t_); } }; template<class F> SubscriptWrapper_t<typename std::decay<F>::type> SubscriptWrapper(F&& f_) { return{std::forward<F>(f_)}; }
J'utilise beaucoup des wrappers comme celui-ci. Ils sont pratiques et ne semblent pas avoir de surcharge de calcul, du moins lorsqu'ils sont compilés par GCC. Vous pouvez en créer un pour à
ou même en créer un pour find
.
EDIT: mis à jour pour C ++ 11 (et mis à jour pour pouvoir retourner une référence)
pas decltype (auto)
en C ++ 14? Dans 11, ce que l'OP a demandé, cela ne fonctionnera pas.
@ Yakk-AdamNevraumont J'aurais dû mieux lire les balises de question. Il est mis à jour maintenant, bien que rendu redondant.
const auto idx_is = make_square_bracket_invoke([](auto&&f){return make_square_bracket_invoke(decltype(f)(f));}); int main() { std::cout << foo( 6, idx_is[[](int x){ return x; }] ) << "\n"; }
Très beau. Une question pour mon édification. Que fait std :: decay
pour un lambda?
@RSahu Si F
est X const &
c'est X
. Si c'est X &
c'est X
. Si c'est X
c'est X
. Sauf si F
est une fonction ou une référence à une fonction ou un tableau ou une référence à un type de tableau. Les lambdas ne sont pas de ceux-là. decay
rend un type adapté au stockage en tant que valeur; par exemple, en le passant dans une fonction.
Merci pour la clarification. Il y a encore de nombreux coins de C ++ 11 que je n'ai pas encore atteints.
Non, ce n'est pas possible. Vous devez passer un objet qui implémente
operator []
, et les objets lambda ne le font pas.