1
votes

Utilisation d'un lambda à la place d'un paramètre de modèle indexable

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 () ?


1 commentaires

Non, ce n'est pas possible. Vous devez passer un objet qui implémente operator [] , et les objets lambda ne le font pas.


4 Réponses :


2
votes

Vous pouvez le faire en:

  1. Créer un modèle de classe, un foncteur, dont l ' opérateur [] est défini.
  2. Implémentation de operator [] en termes de operator () d'une std :: function .
  3. Stockage du lambda dans une 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;
}

Démo en direct

PS

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é.


1 commentaires

Je n'aime pas passer la signature.



0
votes

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.


1 commentaires

Ceci est C ++ 17 - OP demandé 11



1
votes

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)


2 commentaires

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.



4
votes
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";
}

3 commentaires

Très beau. Une question pour mon édification. Que fait std :: decay :: type 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.