我想要一个可变参数的类模板来为每种类型生成一个方法,例如,如下所示的类模板:
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()
应该使用它。
上面的代码抱怨应该至少有两个模板参数,因为存在“两个”基本情况,而不是一个。
答案 0 :(得分:4)
也许其他人可以做得更好,但是我只看到两种方法
递归继承
您可以如下递归定义{{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; };
,该类/结构声明一个
单个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()