从可变 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" 个基本情况而不是一个。
也许其他人可以做得更好,但我只看到两种方法
递归继承
可以递归定义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; };
或
可变继承
您可以定义另一个 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');
}
我想要一个可变 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" 个基本情况而不是一个。
也许其他人可以做得更好,但我只看到两种方法
递归继承
可以递归定义
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; };
或
可变继承
您可以定义另一个 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');
}