从可变 class 模板为每种类型生成一个方法

Generate one method per type from variadic class template

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

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

实例化为 MyClass<double, int> myclass;

时,方法 hello(const double&)hello(const int&) 可用

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

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

这个问题有点类似,但我不明白如何适应我的情况。

编辑

递归继承似乎是适合我的解决方案。这个更复杂的情况怎么样,其中 superclass 有多个方法并且模板参数是必需的?这是我尝试过的方法(但出现错误):

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;
}
}

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

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

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

  1. 递归继承

    可以递归定义MyClass如下

    // 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; };
    

  1. 可变继承

    您可以定义另一个 class/struct,比如 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>)。

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

下面是一个完整的编译示例

#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);
 }

-- 编辑 --

OP 询问

how about a more complicated case where MyClass has more than one method and I always need to have one template argument (see edited question)?

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

但是假设你想要一个纯虚方法,比如 goodmorning() 接收一个 MandT(强制类型),一个纯虚方法 hello() 用于 [=22] 之后的每个类型=] 或当 MandT 之后的列表为空时不带参数的 hello()

可能的解决方案如下

// 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>),则选择主要版本("groundcase with only mandatory type"),因为两个专业化不匹配(没有第一个可选类型)。

如果您使用一种可选类型(例如 MyClass<char, double>)实例化 Myclass,则选择专业化 "groundcase with a single optional type",因为它是最专业的版本。

如果您用两个或更多可选类型实例化一个 MyClass(比如 MyClass<char, double, int> 开始递归(最后的特化)直到保持一个可选类型(所以 "groundcase with a single optional type" 被选中).

请注意,我在两种情况下都放置了 goodmorning(),因为您不需要递归定义它。

下面是一个完整的编译示例

// 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 () {}
 };


struct Derived0 : public MyClass<char>
 {
   void hello () override { }

   char * goodmorning (char const &) override
    { return nullptr; }
 };
struct Derived1 : public MyClass<char, double>
 {
   void hello (double const &) override { }

   char * goodmorning (char const &) override
    { return nullptr; }
 };

struct Derived2 : public MyClass<char, double, int>
 {
   void hello (double const &) override { }
   void hello (int const &) override { }

   char * goodmorning (char const &) override
    { return nullptr; }
 };



int main()
 {
   Derived0  d0;
   Derived1  d1;
   Derived2  d2;

   d0.hello();
   d0.goodmorning('a');

   d1.hello(1.2);
   d1.goodmorning('b');

   d2.hello(3.4);
   d2.hello(5);
   d2.goodmorning('c');
 }