使用模板时是否可以使用多态性?
例如,我有一个名为“Filters”的类,并且我有许多不同的变体/类来过滤数据,因此我根据模板初始化一个对象(哪种类型的过滤器在 main 中定义)
#include "Filter1.h"
#include "Filter2.h"
template<typename T>
class Filters {
public:
void Filter(vector<double> &vec) {
T type;
type.Filter(vec);
}
};
// class Filter1
class Filter1 {
public:
void Filter(vector<double> &vec) {
// Code for "Filter1"
}
};
// MAIN
int main() {
vector<double> sample; // this is a sample vector
Filters<Filter1> exam1;
exam1.filter(sample);
}
但是,在“Filter2”中,假设我们传递了更多参数:
class Filter2 {
public:
void Filter(vector<double> &vec, double point)
{
// Filter 2
}
};
然后是主要内容:
int main()
{
vector<double> sample;
double point = 9;
Filters<Filter2> exam;
exam.Filter(sample, point);
}
这不起作用,因为“Filters”中的“Filter”只接受 1 个参数。
我遇到的问题是过滤器接受的参数不同。例如,“Filter1”传递一个 2D vector 和一个 double,但此类中的 Filter 方法定义仅接受 1D vector 。
我在想(理论上)我可以有一个 switch 语句(“T”)来提供不同类的初始化。
任何想法将不胜感激。
最佳答案
当您使用模板进行通用编程时,您需要针对接口(interface)进行编码。我在这里没有使用该术语的 OOP 含义——而是更广泛的含义。
作为示例,下面是一个针对随机访问迭代器概念类接口(interface)进行编码的函数模板:
template<typename It>
typename std::iterator_traits<It>::value_type
sum(It first, It last)
{
typedef typename std::iterator_traits<It>::difference_type diff_t;
diff_t const size = last - first;
typename std::iterator_traits<It>::value_type accum = 0;
for(diff_t i = 0; i != size; ++i) {
accum += first[i];
}
return accum;
}
(这个例子当然是假的,这里的目的是展示多个随机访问迭代器操作。)
因为我们在契约(Contract)中指定 It
是一个随机访问迭代器,我们知道sum
可以访问:
- 成员类型
std::iterator_traits<It>::value_type
和std::iterator_traits<It>::difference_type
,这些类型可以从operator[]
的结果初始化和operator-
分别 It
上的操作喜欢operator-
和operator[]
,这些不可用于例如双向迭代器
因此sum
可与 std::vector<int>::iterator
一起使用, std::deque<double>::const_iterator
和long*
,它们都是不同的类型,在某些方面可能有所不同,但至少都是随机访问迭代器概念的模型。
(细心的人会注意到,通过使用0
和+=
,我们反过来要求我们的合约中value_type
是一个类似算术的类型。这又是我们编码所针对的接口(interface)!)
然后在设计你的Filters
时您显然打算将其用作 Filters<FilterLike>
,你需要问自己 FilterLike
的常见最小接口(interface)是什么?需要满足。如果有的话FilterX
其中几乎是 FilterLike
除了某些操作之外,这里有一些选项:
正如您在问题中提到的,无论
Filters
使用该特定操作,您可以将其特殊化,以便FilterX
是经过特殊处理的——这可能是你能做的最糟糕的事情。它很脆弱,因为你必须在每个需要操作的站点都执行此操作(即使现在看起来只有一个,将来呢?);不方便,因为不,您不能在类模板函数成员的函数体内打开类型(您必须使用冗长且不明显的不同技术);它引入了耦合Filters
必须了解FilterX
-- 为什么要关心?为
<Filters<FilterX>
编写显式特化。这与上面的很相似,但是当FilterX
时这是一个有趣的选择。差异不仅仅在于一两个操作。与之前的解决方案不同,这并不那么脆弱,因为主模板保持不变,并且所有FilterX
特定的东西放在同一个地方。另一方面,如果FilterX
的一半已经表现得像Filter
,那么这必定意味着有一半的代码Filters<FilterLike>
结束重复或需要一些额外的工作来重构Filters<Filter>
之间的公共(public)代码和Filters<FilterX>
。因此,耦合量会有所不同 - 如果主模板不必了解此显式特化,那么这是一个不错的选择,您甚至不必将显式特化与主模板捆绑在一起写一个
AdaptedFilterX
这是FilterLike
的模型接口(interface)并将其所有操作转发到底层FilterX
。如果您有多个FilterX
,FilterY
这几乎都是Filter
的型号但有一个通用的接口(interface),你可以写一个AdaptedFilter<FilterX>
-- 从某种意义上说AdaptedFilter
模板“转换”FilterXLike
的模型进入FilterLike
的模型
顺便说一句,如果您使用 C++11,您可以编写 Filter
接受任意参数来构造 FilterLike
与:
template<typename... Args>
void Filter(Args&&... args)
{
FilterLike filter(std::forward<Args>(args)...);
// go on using filter...
}
仅接受 FilterLike
可能更简单(并且适用于 C++03)。不过:
void Filter(FilterLike filter)
{
// go on using filter...
}
关于c++ - 将多态性与模板结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13662094/