C ++ универсальный способ определения нескольких функций с помощью шаблона - PullRequest
0 голосов
/ 29 апреля 2018

В C ++ возможно ли определить несколько методов на основе количества предоставленных параметров шаблона? Похоже на то, как работают переменные функции?

С функциями, которые я могу сделать

template <class ...Args>
struct VariadicFunctionCallback {
    typedef std::function<void(std::shared_ptr<Args>...)> variadic;
};

Но я хочу знать, могу ли я сделать нечто подобное, но создать несколько функций вместо нескольких аргументов

template <class ...FunctionArg>
class Example {
     void Function(FunctionArg)...
}

Что позволило бы мне сделать что-то вроде

template <>
class Example<int, float> {
    void Function(int i) {
        ...
    }

    void Function(float f) {
        ...
    }
}

И если это возможно, каковы преимущества перед моей текущей настройкой, которая похожа на

template<class EventType>
class EventHandler {
public:
    void HandleEvent(const std::shared_ptr<EventType>& event) {
    }
};

class ExampleEvent : public Event<ExampleEvent> {

};

class ExampleHandler : public EventHandler<ExampleHandler>, EventHandler<Events::ShutdownEvent> {
public:
    void HandleEvent(const std::shared_ptr<ExampleEvent> &event);

    void HandleEvent(const std::shared_ptr<Events::ShutdownEvent> &event);
};

- Изменить - Я закончил со смесью, если два решения. Вероятно, это не самый лучший вариант, и я буду продолжать тренироваться и улучшать его со временем.

template <class EventType>
class BaseEventHandler {
public:
    EventIdentifier GetIdentifier() const {
        return EventType::GetIdentifier();
    }

    virtual void HandleEvent(const std::shared_ptr<EventType> &event) = 0;
};

template<class EventType, class ...EventTypes>
class EventHandler: public BaseEventHandler<EventTypes>... {

};

Что тогда позволяет мне сделать

class EventListener: public EventHandler<ShutdownEvent, MousePosEvent, WindowCloseRequestEvent> {
    void HandleEvent(const std::shared_ptr<ShutdownEvent> &event);
    void HandleEvent(const std::shared_ptr<MousePosEvent> &event);
    void HandleEvent(const std::shared_ptr<WindowCloseRequestEvent> &event);
}

Ответы [ 2 ]

0 голосов
/ 30 апреля 2018

Этот ответ начинается с ответа max66 , более или менее

Мы начнем с класса, который использует рекурсивное наследование для реализации нашей функции. В моем случае я выбрал operator() и использовал объявление с переменным числом, чтобы ввести в область действия все дочерние элементы operator():

namespace detail{
    template<class T, class... U>
    struct ExampleImpl : ExampleImpl<U>...{
        using ExampleImpl<U>::operator()...;
        void operator()(T _arg){/*...*/}
    };
}

Когда мой ответ отличается от max66, мы используем этот класс ExampleImpl для compose нашего Example класса:

template<class... T>
class Example
{
public:
    template <class U>
    void Function(U arg)
    {
        impl(arg);
    }

    void Function(float arg) 
    {
        /*Your specialization code*/
    }
private:
    detail::ExampleImpl<T...> impl;
};

Я делаю это по двум причинам:

  1. Это наследование - это деталь реализации, которую вы хотели бы скрыть от клиентов.
  2. * Теперь мы можем легко специализировать нашу функцию Function для любого типа, который мы хотим, потому что у нас всегда есть выбор: вызывать ли наш экземпляр ExampleImpl или нет.

Демо

Если ExampleImpl необходимо использовать переменные-члены вашего класса Example, то вы можете либо превратить ExampleImpl в полноценный PIMPL класс, либо изменить его конструктор или operator(), чтобы принять дополнительные аргументы в виде Внедрение зависимостей

* Вы также можете легко выполнить полную специализацию класса, где float является одним из параметров шаблона в специализации, и определить свой собственный Function. Или вы можете использовать форму для отправки тега , чтобы скрыть версию float, если она не была в списке типов шаблонов.

Демонстрация отправки тегов

0 голосов
/ 29 апреля 2018

Полагаю, вы можете сделать Example своего рода рекурсивным классом самонаследования; что-то вроде

    template <typename ...>
    struct Example
     {
       // dummy Function() to end the recursion
       void Function ()
        { }
     };

    template <typename T0, typename ... Ts>
    struct Example<T0, Ts...> : public Example<Ts...>
     {
       using Example<Ts...>::Function;

       void Function (T0 const &)
        { };
     };

Так что вы можете написать

int main ()
 {
   Example<int, long, float>  e0;

   e0.Function(0);
   e0.Function(0L);
   e0.Function(0.0f);
 }

- РЕДАКТИРОВАТЬ -

ОП спросить

Может ли тогда быть сформирована специализация?

Вы имеете в виду что-то следующее?

template <typename ...>
struct Example
 {
   // dummy Function() to end the recursion
   void Function ()
    { }
 };

template <typename T0, typename ... Ts>
struct Example<T0, Ts...> : public Example<Ts...>
 {
   using Example<Ts...>::Function;

   void Function (T0 const &)
    { };
 };

template <typename ... Ts>
struct Example<float, Ts...> : public Example<Ts...>
 {
   void FunctionFloat (float const &)
    { };
 };

int main ()
 {
   Example<int, long, float>  e0;

   e0.Function(0);
   e0.Function(0L);
   e0.FunctionFloat(0.0f);
   //e0.Function(0.0f); // compilation error
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...