用模板定义多个函数的C ++泛型方法

时间:2018-04-29 01:55:50

标签: c++ templates variadic-templates c++17 template-specialization

在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 :(得分:8)

我想你可以让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);
 }

- 编辑 -

OP问

  

然后可以在此基础上进行专业化吗?

你的意思是如下吗?

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
 }

答案 1 :(得分:2)

This answer start's with max66's answer,或多或少

我们从一个使用递归继承来实现我们的函数的类开始。就我而言,我选择了operator(),并使用了一个变量使用声明来将所有孩子operator()纳入范围:

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

我的回答与max66相反,我们会使用此ExampleImpl类来撰写我们的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实例。
  3. Demo

    如果ExampleImpl需要使用Example类的成员变量,那么您可以将ExampleImpl转换为完整的PIMPL类,或者修改其构造函数或operator()Dependency Injection

    的形式接受其他参数

    *您可以轻松执行全班专精,其中float是专业化中的模板参数之一,并定义您自己的Function。或者您可以使用tag dispatching形式隐藏float版本,除非它位于模板类型列表中。

    Tag dispatch demonstration