模板子类化的例子
An example of template subclassing
我正在阅读 SEQAN 文档。在入门部分,他们提到 "Template Subclassing" 作为克服运行时多态性开销的方法。
OOP vs. Generic Programming:
In SeqAn, we use a technique called template subclassing which is based on generic programming. This technique provides polymorphism into C++ programs at compile time using templates. Such static polymorphism is different from runtime polymorphism which is supported in C++ using subclassing and virtual functions. It comes at the cost of some additional typing but has the advantage that the compiler can inline all function calls and thus achieve better performance. An example will be given in the section “From OOP to SeqAn” in the First Steps Tutorial.
遗憾的是,目前还没有例子来说明它的用法。如果有人提供一个简单的例子,我真的很感激。
我写了简单的模板 class 但我不确定这是否是模板子 classing 的意思!
struct Base {
virtual void nockNock() {
std::cout << "I am Base class." << std::endl;
}
};
template<typename T>
class Derived: public Base {
public:
void anotherNock() {
std::cout << "It's me the Derived class." << std::endl;
}
void nockNock() {
std::cout << "I am Derived class." << std::endl;
anotherNock();
}
public:
Derived(){};
};
int main() {
Base* myArray[10];
myArray[0] = new Derived<int>;
myArray[0]->nockNock();
return 0;
}
在简要浏览了 SeqAn 手册后,我想到了以下示例:
namespace Tags {
struct Noone {};
struct Someone {};
}
template <typename T, typename U>
class Base {
public:
using KindOfThing = U;
// implement algorithm
bool knock() {
static_cast<T*>(this)->knockKnock();
static_cast<T*>(this)->listen();
static_cast<T*>(this)->knockKnock();
static_cast<T*>(this)->tellName("Johnny");
return static_cast<T*>(this)->knockKnock();
}
};
template <typename T>
class Base<T, Tags::Noone> {
public:
using KindOfThing = Tags::Noone;
void work() {
static_cast<T*>(this)->makeSounds();
static_cast<T*>(this)->doJob();
}
};
class WashingMachine : public Base<WashingMachine, Tags::Noone> {
public:
void makeSounds(){};
void doJob(){};
};
class Meow : public Base<Meow, Tags::Someone> {
public:
Meow() : meows(0) {}
//implement Base interface
void listen() { std::cout << "..." << std::endl; }
//implement Base interface
bool knockKnock() {
std::cout << "Meow..." << std::endl;
return meows++ > 3;
}
void tellName(std::string const& name) { (void)name; }
int meows;
};
class WhoIsThere : public Base<WhoIsThere, Tags::Someone> {
public:
//implement Base interface
void listen() { std::cout << "<Steps>..." << std::endl; }
//implement Base interface
bool knockKnock() {
std::cout << "WhoIsThere?" << std::endl;
return isDone;
}
void tellName(std::string const& name) {
isDone = true;
std::cout << name + " is here))!" << std::endl;
}
bool isDone;
};
template <typename T, typename U>
void performKnocking(T&& item, U) {
std::cout << "......" << std::endl;
while (!item.knock())
;
}
template <typename T>
void performKnocking(T&& item, Tags::Noone) {
std::cout << "Noone" << std::endl;
}
template <typename... TArgs>
void performKnockingToEveryone(TArgs&&... sequence) {
int dummy[] = {(performKnocking(sequence, typename TArgs::KindOfThing()), 0)...};
}
int main() {
performKnockingToEveryone(
Meow(), WashingMachine(), WhoIsThere(), Meow(), WashingMachine());
return 0;
}
重点是 SeqAn 所讲述的设计正在从通常的 OOP 编程领域(具有多态性、类型抽象等)转移到 C++ 模板编程(更准确地参见 Effective C++). The same like there are zellions of books on OOP design, not less, if not more, there are material on Template C++ programming (see The Definitive C++ Book Guide and List 中的第 1 项)两种编程技术以及更多)。
在这个例子中,我展示了 SeqAn 文档强调的两种设计技术。
有一个实现某种操作的基类,该操作具有其他子类共有的几个步骤。基础提供了一种 Template Method (there is nothing related to C++ templates there, it is purely OOP pattern, just the name has the same word) from operations - setting an interface (not OOP interface, but C++ Template interface) for the deriving classes. Those subclasses, in their turn, implement those operations. You get static (resolved at compile time behaviour) polymorphism (type that has an interface that has different behavior from one instance to another). There is no runtime polymorphism because for runtime (OOP realm) those all are different types, they are all different for compile time as well. This polymorphism exists in C++ templates (templates parameters and templates declarations). Example: Boost Iterator Facade.
标签调度 技术根据参数类型采用函数重载解决方案。当你有自由功能作为你的 API 的一部分并根据情况选择必要的一个来调用时它会有所帮助(例如 STL Iterators iterator_tag
(s) and iterator free functions). For exmaple, consider OOP Visitor Pattern 并且你有 AbstractVisitor
和 KnockingVisitor
。你拿一个Base*
的矢量并在每个上调用 accept
,调用 visit
和你的 KnockingVisitor
敲门。通过标签调度,你使用标签。这可能是 OOP 之间的蹩脚比较技术和标记调度,但这只是许多可能示例中的一个。就像 OOP 设计在何处以及使用何种模式一样,您需要经验使用 C++ 模板技术。
这个例子很原始,因为它背后没有实际的任务是设计,但是,当你有一个雄心勃勃的目标时,它会变得更加有趣和复杂。 C++ 模板可能是归档它的选择之一。
There’s an existing question/answer that describes what template subclassing means, and how it’s used in SeqAn。让我在这里举一个更简单的例子。
// No default `Spec` given = “virtual” base class!
template <typename Spec> struct Fruit {};
template <typename Spec>
inline void eat(Fruit<Spec> const&) {
std::cout << "nibbling an unknown fruit\n";
}
请注意,模板 subclassing 中的方法 始终 自由函数,因为方法分派严格通过重载工作,而不是像传统 sub[=78 中那样重写=]ing.
这为 Fruit
定义了一个基础 class,以及一个方法 eat
,可以在任何水果上调用。
我们现在将定义专业层次结构(“subclasses”)。第一,简单的;这些只是类型标签,将被插入到 Spec
模板参数中:
struct Orange;
struct Apple;
就是这样。我们甚至不需要在我们的示例中定义这些标签,声明它们就足够了(但对于更复杂的情况,定义可能变得必要;在 SeqAn 中,类型标签总是被定义)。
现在,这里是 Apple
s 的 eat
方法的覆盖:
template <>
inline void eat(Fruit<Apple> const&) {
std::cout << "scrunching an apple\n";
}
由于我们不覆盖 Orange
的方法,因此它们将始终调用基础 class 方法。
这里有一些更多的专业化,用于柑橘类水果(子class 等级):
struct Lemon;
struct Lime;
template <typename Spec = Lemon> struct Citrus { };
请注意,与 Apple
s 和 Orange
s 不同,Citrus
本身就是一个可以被子classed 的模板。我们现在可以像以前一样为 Citrus
水果覆盖 eat
,但我想展示一个 subclass 方法如何分派给一个基本 class 方法;为此,让我们引入一个辅助函数模板 eat_citrus
,它将从 eat
:
调用
template <typename Spec>
inline void eat(Fruit<Citrus<Spec>> const&) {
eat_citrus<Spec>();
}
这是 eat_citrus
的基本 class 定义,适用于任何 Citrus
:
template <typename Tag = Lemon>
inline void eat_citrus() {
std::cout << "ew, that’s sour!\n";
}
下面是 Lime
s 的覆盖:
template <>
inline void eat_citrus<Lime>() {
std::cout << "nice taste, but ";
eat_citrus<>(); // Calls the base class method.
}
最后,如果我们使用这些 classes 如下:
// Does not work, since `Fruit` is “virtual”:
// Fruit<> fruit;
Fruit<Orange> orange;
Fruit<Apple> apple;
Fruit<Citrus<>> lemon;
Fruit<Citrus<Lime>> lime;
eat(orange);
eat(apple);
eat(lemon);
eat(lime);
… 我们得到这个输出:
nibbling an unknown fruit
scrunching an apple
ew, that’s sour!
nice taste, but ew, that’s sour!
在SeqAn中,上面的做法略有不同;为了简单起见,我在这里更改了它,老实说,因为我已经多年没有使用 SeqAn 了,我不记得细节了。如果我没记错的话,eat_citrus
实际上并不是必需的,这里将使用标签分派 + 重载(如 Yuki 所提到的)而不是模板特化。
另请注意,我的代码没有使用 C++ 术语中的任何实际继承(即 Fruit<Apple>
不继承自 Fruit<whatever>
)。这不是绝对必要的,但通常非常有用,IIRC 大多数 SeqAn class 模板 do 实际上也继承了它们的基础 class.
我正在阅读 SEQAN 文档。在入门部分,他们提到 "Template Subclassing" 作为克服运行时多态性开销的方法。
OOP vs. Generic Programming: In SeqAn, we use a technique called template subclassing which is based on generic programming. This technique provides polymorphism into C++ programs at compile time using templates. Such static polymorphism is different from runtime polymorphism which is supported in C++ using subclassing and virtual functions. It comes at the cost of some additional typing but has the advantage that the compiler can inline all function calls and thus achieve better performance. An example will be given in the section “From OOP to SeqAn” in the First Steps Tutorial.
遗憾的是,目前还没有例子来说明它的用法。如果有人提供一个简单的例子,我真的很感激。
我写了简单的模板 class 但我不确定这是否是模板子 classing 的意思!
struct Base {
virtual void nockNock() {
std::cout << "I am Base class." << std::endl;
}
};
template<typename T>
class Derived: public Base {
public:
void anotherNock() {
std::cout << "It's me the Derived class." << std::endl;
}
void nockNock() {
std::cout << "I am Derived class." << std::endl;
anotherNock();
}
public:
Derived(){};
};
int main() {
Base* myArray[10];
myArray[0] = new Derived<int>;
myArray[0]->nockNock();
return 0;
}
在简要浏览了 SeqAn 手册后,我想到了以下示例:
namespace Tags {
struct Noone {};
struct Someone {};
}
template <typename T, typename U>
class Base {
public:
using KindOfThing = U;
// implement algorithm
bool knock() {
static_cast<T*>(this)->knockKnock();
static_cast<T*>(this)->listen();
static_cast<T*>(this)->knockKnock();
static_cast<T*>(this)->tellName("Johnny");
return static_cast<T*>(this)->knockKnock();
}
};
template <typename T>
class Base<T, Tags::Noone> {
public:
using KindOfThing = Tags::Noone;
void work() {
static_cast<T*>(this)->makeSounds();
static_cast<T*>(this)->doJob();
}
};
class WashingMachine : public Base<WashingMachine, Tags::Noone> {
public:
void makeSounds(){};
void doJob(){};
};
class Meow : public Base<Meow, Tags::Someone> {
public:
Meow() : meows(0) {}
//implement Base interface
void listen() { std::cout << "..." << std::endl; }
//implement Base interface
bool knockKnock() {
std::cout << "Meow..." << std::endl;
return meows++ > 3;
}
void tellName(std::string const& name) { (void)name; }
int meows;
};
class WhoIsThere : public Base<WhoIsThere, Tags::Someone> {
public:
//implement Base interface
void listen() { std::cout << "<Steps>..." << std::endl; }
//implement Base interface
bool knockKnock() {
std::cout << "WhoIsThere?" << std::endl;
return isDone;
}
void tellName(std::string const& name) {
isDone = true;
std::cout << name + " is here))!" << std::endl;
}
bool isDone;
};
template <typename T, typename U>
void performKnocking(T&& item, U) {
std::cout << "......" << std::endl;
while (!item.knock())
;
}
template <typename T>
void performKnocking(T&& item, Tags::Noone) {
std::cout << "Noone" << std::endl;
}
template <typename... TArgs>
void performKnockingToEveryone(TArgs&&... sequence) {
int dummy[] = {(performKnocking(sequence, typename TArgs::KindOfThing()), 0)...};
}
int main() {
performKnockingToEveryone(
Meow(), WashingMachine(), WhoIsThere(), Meow(), WashingMachine());
return 0;
}
重点是 SeqAn 所讲述的设计正在从通常的 OOP 编程领域(具有多态性、类型抽象等)转移到 C++ 模板编程(更准确地参见 Effective C++). The same like there are zellions of books on OOP design, not less, if not more, there are material on Template C++ programming (see The Definitive C++ Book Guide and List 中的第 1 项)两种编程技术以及更多)。
在这个例子中,我展示了 SeqAn 文档强调的两种设计技术。
有一个实现某种操作的基类,该操作具有其他子类共有的几个步骤。基础提供了一种 Template Method (there is nothing related to C++ templates there, it is purely OOP pattern, just the name has the same word) from operations - setting an interface (not OOP interface, but C++ Template interface) for the deriving classes. Those subclasses, in their turn, implement those operations. You get static (resolved at compile time behaviour) polymorphism (type that has an interface that has different behavior from one instance to another). There is no runtime polymorphism because for runtime (OOP realm) those all are different types, they are all different for compile time as well. This polymorphism exists in C++ templates (templates parameters and templates declarations). Example: Boost Iterator Facade.
标签调度 技术根据参数类型采用函数重载解决方案。当你有自由功能作为你的 API 的一部分并根据情况选择必要的一个来调用时它会有所帮助(例如 STL Iterators
iterator_tag
(s) and iterator free functions). For exmaple, consider OOP Visitor Pattern 并且你有AbstractVisitor
和KnockingVisitor
。你拿一个Base*
的矢量并在每个上调用accept
,调用visit
和你的KnockingVisitor
敲门。通过标签调度,你使用标签。这可能是 OOP 之间的蹩脚比较技术和标记调度,但这只是许多可能示例中的一个。就像 OOP 设计在何处以及使用何种模式一样,您需要经验使用 C++ 模板技术。
这个例子很原始,因为它背后没有实际的任务是设计,但是,当你有一个雄心勃勃的目标时,它会变得更加有趣和复杂。 C++ 模板可能是归档它的选择之一。
There’s an existing question/answer that describes what template subclassing means, and how it’s used in SeqAn。让我在这里举一个更简单的例子。
// No default `Spec` given = “virtual” base class!
template <typename Spec> struct Fruit {};
template <typename Spec>
inline void eat(Fruit<Spec> const&) {
std::cout << "nibbling an unknown fruit\n";
}
请注意,模板 subclassing 中的方法 始终 自由函数,因为方法分派严格通过重载工作,而不是像传统 sub[=78 中那样重写=]ing.
这为 Fruit
定义了一个基础 class,以及一个方法 eat
,可以在任何水果上调用。
我们现在将定义专业层次结构(“subclasses”)。第一,简单的;这些只是类型标签,将被插入到 Spec
模板参数中:
struct Orange;
struct Apple;
就是这样。我们甚至不需要在我们的示例中定义这些标签,声明它们就足够了(但对于更复杂的情况,定义可能变得必要;在 SeqAn 中,类型标签总是被定义)。
现在,这里是 Apple
s 的 eat
方法的覆盖:
template <>
inline void eat(Fruit<Apple> const&) {
std::cout << "scrunching an apple\n";
}
由于我们不覆盖 Orange
的方法,因此它们将始终调用基础 class 方法。
这里有一些更多的专业化,用于柑橘类水果(子class 等级):
struct Lemon;
struct Lime;
template <typename Spec = Lemon> struct Citrus { };
请注意,与 Apple
s 和 Orange
s 不同,Citrus
本身就是一个可以被子classed 的模板。我们现在可以像以前一样为 Citrus
水果覆盖 eat
,但我想展示一个 subclass 方法如何分派给一个基本 class 方法;为此,让我们引入一个辅助函数模板 eat_citrus
,它将从 eat
:
template <typename Spec>
inline void eat(Fruit<Citrus<Spec>> const&) {
eat_citrus<Spec>();
}
这是 eat_citrus
的基本 class 定义,适用于任何 Citrus
:
template <typename Tag = Lemon>
inline void eat_citrus() {
std::cout << "ew, that’s sour!\n";
}
下面是 Lime
s 的覆盖:
template <>
inline void eat_citrus<Lime>() {
std::cout << "nice taste, but ";
eat_citrus<>(); // Calls the base class method.
}
最后,如果我们使用这些 classes 如下:
// Does not work, since `Fruit` is “virtual”:
// Fruit<> fruit;
Fruit<Orange> orange;
Fruit<Apple> apple;
Fruit<Citrus<>> lemon;
Fruit<Citrus<Lime>> lime;
eat(orange);
eat(apple);
eat(lemon);
eat(lime);
… 我们得到这个输出:
nibbling an unknown fruit
scrunching an apple
ew, that’s sour!
nice taste, but ew, that’s sour!
在SeqAn中,上面的做法略有不同;为了简单起见,我在这里更改了它,老实说,因为我已经多年没有使用 SeqAn 了,我不记得细节了。如果我没记错的话,eat_citrus
实际上并不是必需的,这里将使用标签分派 + 重载(如 Yuki 所提到的)而不是模板特化。
另请注意,我的代码没有使用 C++ 术语中的任何实际继承(即 Fruit<Apple>
不继承自 Fruit<whatever>
)。这不是绝对必要的,但通常非常有用,IIRC 大多数 SeqAn class 模板 do 实际上也继承了它们的基础 class.