2 votes

Modèles C++ : faire quelque chose à l'intérieur d'une fonction, en fonction du paramètre du modèle.

Jetez un coup d'œil à la fonction membre virtual std::future<void> DoInvoke () de la classe EventMemberImpl :

#include <future>
#include <functional>

class EventImpl
{
public:
  virtual ~EventImpl () = default;
  std::future<void> Invoke () { return DoInvoke (); }
private:
  virtual std::future<void> DoInvoke () = 0;
};

template <typename... Ts>
EventImpl * MakeEvent (Ts&&... args)
{
  class EventMemberImpl : public EventImpl
  {
public:
    EventMemberImpl (Ts&&... args)
      : m_function (std::bind (std::forward<Ts> (args)...))
    {
    }
protected:
    virtual ~EventMemberImpl ()
    {
    }
private:
    virtual std::future<void> DoInvoke ()
    {
      // MakeEvent has been called with
      // a Callable which does not return a future
      m_function ();
      return std::future<void> ();
      // OR
      // MakeEvent has been called with a
      // Callable which returns a future:
      return m_function();
    }
     // In case is any function...  :
    std::function<void ()> m_function;

    // OR
    // ... except when it returns a future:
    std::function<std::future<void> ()> m_function;
  };
  return new EventMemberImpl (std::forward<Ts> (args)...);
}

int foo() { return 5; }
std::future<int> baz () { return std::future<int> (); }

int main ()
{

  EventImpl *event1 = MakeEvent(std::forward<decltype (&foo)> (&foo));
  EventImpl *event2 = MakeEvent(std::forward<decltype (&baz)> (&baz));

  return 0;
}

Ce que je voudrais faire, c'est que lorsque quelqu'un appelle MakeEvent avec une fonction (n'importe quel appelable) comme paramètre qui retourne void (ou presque n'importe quoi d'autre), le corps devrait se comporter exactement comme je l'ai écrit. D'autre part, lorsque la fonction MakeEvent est appelée avec une fonction comme paramètre qui retourne un std::future, alors elle devrait faire des choses différentes.

1voto

Guillaume Racicot Points 1106

Vous pouvez utiliser un mélange de std::conditional et la surcharge des fonctions.

Définissons un alias pour le type de retour de la fonction :

using result = decltype(std::bind(std::declval<Ts>()...)());

Ensuite, nous pouvons créer un autre alias pour nos conditions :

template<typename T>
using is_future = std::is_convertible<T, std::future<void>>;

Ensuite, avec cette condition, nous pouvons déclarer notre membre :

std::conditional_t<is_future<result>::value,
    std::function<std::future<void>>,
    std::function<void()>
> m_function;

Ensuite, nous pouvons surcharger une fonction pour sélectionner conditionnellement l'expression nécessaire. En C++17, une constexpr n'est utilisée que si elle est nécessaire :

virtual std::future<void> DoInvoke () {
    if constexpr (!is_future<result>::value) {
        m_function ();
        return std::future<void>();
    } else {
        return m_function();
    }
}

Contrairement à C++14, vous devez surcharger la fonction et utiliser sfinae :

template<typename F, std::enable_if_t<is_future<std::result_of_t<F()>>::value>* = nullptr>
auto DoInvokeSelect(F& function) -> std::future<void> {
    return function();
}

template<typename F, std::enable_if_t<!is_future<std::result_of_t<F()>>::value>* = nullptr>
auto DoInvokeSelect(F& function) -> std::future<void> {
    function();
    return std::future<void>();
}

virtual std::future<void> DoInvoke () {
    return DoInvokeSelect(m_function);
}

Le paramètre enable_if sélectionne l'une ou l'autre des fonctions si le résultat est un futur ou non.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X