从可变参量类模板为每种类型生成一种方法

时间:2019-07-11 18:38:05

标签: c++11 methods overloading abstract-class variadic-templates

我想要一个可变参数的类模板来为每种类型生成一个方法,例如,如下所示的类模板:

template <class T, class ... Ts>
class MyClass {
public:
    virtual void hello(const T& t) = 0;
};

在实例化为hello(const double&)时将使方法hello(const int&)MyClass<double, int> myclass;可用

请注意,我希望类是纯抽象的,以便派生类实际上需要执行实现,例如:

class Derived : MyClass<double, int> {
public:
    inline void hello(const double& t) override { }
    inline void hello(const int& t) override { }
};

这个问题与to this one有点相似,但是我不明白如何使它适应我的情况。

编辑

递归继承对我来说似乎是正确的解决方案。如果父类具有多个方法且必须使用模板参数,那么这种更复杂的情况如何?这是我尝试过的方法(但出现错误):

template <class MandatoryT, class OptionalT, class... MoreTs>
class MyClass : public MyClass<MandatoryT, MoreTs...> {
public:
    virtual ~MyClass() {}

    virtual char* goodmorning(const MandatoryT& t) = 0;

    virtual bool bye(const MandatoryT& t,
                     const std::map<std::string,bool>& t2) = 0;

  using MyClass<MandatoryT, MoreTs...>::hello;
  virtual void hello(const OptionalT& msg) = 0;
};


template <class MandatoryT, class OptionalT>
class MyClass<MandatoryT, OptionalT> {
  virtual void processSecondaryMessage(const OptionalT& msg) = 0;
};

template <class MandatoryT>
class MyClass<MandatoryT> {
  virtual void processSecondaryMessage() = 0;
}
}

基本上我想要的是派生类应该具有一种或多种类型。第一个用于其他方法,而从第二个开始,应在hello()中使用。如果仅提供一种类型,则会调用一个空的hello()。但是,如果至少提供了第二种类型,则hello()应该使用它。

上面的代码抱怨应该至少有两个模板参数,因为存在“两个”基本情况,而不是一个。

1 个答案:

答案 0 :(得分:4)

也许其他人可以做得更好,但是我只看到两种方法

  1. 递归继承

    您可以如下递归定义{{​​1}}

    MyClass

  1. 可变继承

    您可以定义另一个类/结构,例如// recursive case template <typename T, typename ... Ts> struct MyClass : public MyClass<Ts...> { using MyClass<Ts...>::hello; virtual void hello (const T&) = 0; }; // ground case template <typename T> struct MyClass<T> { virtual void hello (const T&) = 0; }; ,该类/结构声明一个 单个MyHello方法,可变参数从hello()继承。

    MyClass

该递归示例与类型冲突兼容(即:当模板参数template <typename T> struct MyHello { virtual void hello (const T&) = 0; }; template <typename ... Ts> struct MyClass : public MyHello<Ts>... { }; ;例如MyClass的列表中存在类型较多的时间时,该方法也适用)。

不幸的是,可变参数继承案并非如此。

以下是完整的编译示例

MyClass<int, double, int>

-编辑-

OP询问

  

在MyClass具有多个方法并且我总是需要一个模板参数的情况下,情况如何呢?

从您的问题中我不明白您到底想要什么。

但是假设您想要一个纯虚拟方法,比如说#if 1 // recursive case template <typename T, typename ... Ts> struct MyClass : public MyClass<Ts...> { using MyClass<Ts...>::hello; virtual void hello (const T&) = 0; }; // ground case template <typename T> struct MyClass<T> { virtual void hello (const T&) = 0; }; #else template <typename T> struct MyHello { virtual void hello (const T&) = 0; }; template <typename ... Ts> struct MyClass : public MyHello<Ts>... { }; #endif struct Derived : public MyClass<double, int> { inline void hello (const double&) override { } inline void hello (const int&) override { } }; int main() { Derived d; d.hello(1.0); d.hello(2); } 会收到goodmorning()(强制类型),那么纯净虚拟方法MandT会跟随hello()之后的每种类型或MandT之后的列表为空时,没有参数的hello()

可能的解决方法是以下

MandT

这里递归比以前复杂一些。

如果仅实例化强制类型的// declaration and groundcase with only mandatory type (other cases // intecepted by specializations) template <typename MandT, typename ...> struct MyClass { virtual void hello () = 0; virtual ~MyClass () {} virtual char * goodmorning (MandT const &) = 0; }; // groundcase with a single optional type template <typename MandT, typename OptT> struct MyClass<MandT, OptT> { virtual void hello (OptT const &) = 0; virtual ~MyClass () {} virtual char * goodmorning (MandT const &) = 0; }; // recursive case template <typename MandT, typename OptT, typename ... MoreOptTs> struct MyClass<MandT, OptT, MoreOptTs...> : public MyClass<MandT, MoreOptTs...> { using MyClass<MandT, MoreOptTs...>::hello; virtual void hello (OptT const &) = 0; virtual ~MyClass () {} }; (例如:MyClass),则会选择主版本(“仅强制类型的基本情况”),因为这两个特殊化不匹配(没有第一个可选类型)。

如果用一种可选类型(例如MyClass<char>实例化Myclass,则选择特殊化“具有单个可选类型的groundcase”,因为它是最特殊的版本。

如果您实例化具有两个或多个可选类型的MyClass<char, double>(例如,MyClass开始递归(最后一个专业化),直到保持单个可选类型(因此,“具有单个可选类型的基础”选择)。

请注意,在两种情况下我都将MyClass<char, double, int>放在了上面,因为您无需递归定义它。

以下是完整的编译示例

goodmorning()