Exemple simple de délégué

Un délégué est un objet capable de « contenir » une entité susceptible d'être appelée, qu'il s'agisse d'une fonction globale, d'une méthode de classe ou d'une méthode d'instance (pour une instance donnée). Je présume qu'on pourrait aussi y déposer une λ mais je n'y ai pas suffisamment réfléchi pour être en mesure (a) de l'affirmer ou (b) d'offrir un exemple opérationnel.

L'exemple proposé ci-dessus applique un variante de l'idiome pImpl avec une strate utilisable (delegue), une strate d'abstraction (invoquable, qui est elle-même Clonable) et une strate d'implémentation (fonction et methode_instance). La strate utilisable est une classe englobante et toutes les autres sont privées et internes.

Cette version utilise de l'allocation dynamique de mémoire. Ceci signifie qu'il nous faut implémenter la Sainte-Trinité, mais que la sémantique de mouvement y est banale à implémenter (ce que j'ai fait, évidemment).

Dans cette implémentation, le recours à new prend un temps indéterministe, mais que la sémantique de mouvement s'implémente en temps constant (complexité ).

#include <functional>
#include <utility>
#include <cassert>
#include <iostream>
#include <memory>
template <class R, class A>
class delegue {
   struct invoquable {
      virtual R invoquer(A) = 0;
      virtual invoquable* cloner() const = 0;
      virtual ~invoquable() = default;
   };
   class fonction : public invoquable {
      R(*ptr)(A);
   public:
      fonction(R(*ptr)(A)) noexcept : ptr{ ptr } {
      }
      R invoquer(A arg) {
         return ptr(arg);
      }
      fonction* cloner() const override {
         return new fonction{ *this };
      }
   };
   template <class T>
   class methode_instance : public invoquable {
      T *inst;
      R(T::*ptr)(A);
   public:
      methode_instance(T *inst, R(T::*ptr)(A)) noexcept : inst{ inst }, ptr{ ptr } {
      }
      R invoquer(A arg) {
         return (inst->*ptr)(arg);
      }
      methode_instance* cloner() const override {
         return new methode_instance{ *this };
      }
   };
   template <class T>
   class methode_instance_const : public invoquable {
      T *inst;
      R(T::*ptr)(A) const;
   public:
      methode_instance_const(T *inst, R(T::*ptr)(A) const) noexcept : inst{ inst }, ptr{ ptr } {
      }
      R invoquer(A arg) {
         return (inst->*ptr)(arg);
      }
      methode_instance_const* cloner() const override {
         return new methode_instance_const{ *this };
      }
   };
   std::unique_ptr<invoquable> ptr;
public:
   bool empty() const noexcept {
      return !ptr;
   }
   delegue() = default;
   delegue(R(*fct)(A)) : ptr{ new fonction{ fct } } {
   }
   template<class T>
   delegue(T *inst, R(T::*fct)(A)) : ptr{ new methode_instance<T>{ inst, fct } } {
   }
   template<class T>
   delegue(T *inst, R(T::*fct)(A) const) : ptr{ new methode_instance_const<T>{ inst, fct } } {
   }
   void swap(delegue &d) noexcept {
      using std::swap;
      swap(ptr, d.ptr);
   }
   delegue(const delegue &autre) : ptr{ autre.empty()? nullptr : autre.ptr->cloner() } {
   }
   delegue(delegue &&) = default;
   delegue &operator=(const delegue &autre) {
      delegue{ autre }.swap(*this);
      return *this;
   }
   delegue &operator=(delegue &&) = default;
   ~delegue() = default;
   // invocation
   R operator()(A arg) {
      assert(!empty());
      return ptr->invoquer(arg);
   }
};

class X {
   double val;
public:
   X(double val) : val{ val } {
   }
   double m(int x) {
      return x + val++;
   }
   double mc(int x) const {
      return x + val;
   }
};

double f(int x) {
   return static_cast<double>(x) / 2.0;
}

int main() {
   using namespace std;
   delegue<double, int> d = f;
   cout << d(3) << endl;
   X x{ 0.5 };
   d = delegue<double, int>(&x, &X::m);
   cout << d(3) << endl;
   d = delegue<double, int>(&x, &X::mc);
   cout << d(3) << endl;
}

Valid XHTML 1.0 Transitional

CSS Valide !