2

使用模板时是否可以使用 PolyMorphism?

例如,我有一个名为“过滤器”的类,我有许多不同的数据过滤方式/类,因此我根据模板初始化一个对象(主要定义了哪种类型的过滤器)

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

这是行不通的,因为“过滤器”中的“过滤器”只接受 1 个参数。

我遇到的问题是过滤器接受的参数不同。例如,“Filter1”通过一个 2D 向量和一个 double 但此类中的 Filter 方法定义只接受一个 1D 向量。

我在想(理论上)我可以有一个 switch 语句(“T”)来提供初始化不同的类。

任何想法,将不胜感激 。

4

2 回答 2

1

如果您依赖模板,另一种方法是将过滤器作为参数传递该模板。大多数过滤器都依赖于标准操作,如 + - /。因此,您可以在 Vector2D、Vector1D ... 类中重载该函数,并且您的过滤器函数将自动调用该方法。

希望这个对你有帮助 :)

于 2012-12-01T18:14:05.297 回答
1

当您使用模板进行通用编程时,您需要针对接口进行编码。我在这里没有使用该术语的 OOP 含义——而是更广泛的含义。

例如,下面是一个针对 Random-Access Iterator 类似概念的接口进行编码的函数模板:

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

(这个例子当然是假的,这里的目的是展示多个 Random-Access Iterator 操作。)

因为我们在合约中指定了It一个随机访问迭代器,所以我们知道sum可以访问:

  • 成员类型std::iterator_traits<It>::value_typestd::iterator_traits<It>::difference_type,它们是可以分别从 和 的结果初始化的operator[]类型operator-
  • Itlikeoperator-和上的操作operator[],例如双向迭代器不可

因此sum可以与std::vector<int>::iterator,std::deque<double>::const_iterator和一起使用long*,它们都是不同的类型,在某些方面可能不同,但至少是随机访问迭代器概念的所有模型。

(观察者会注意到,通过使用0and+=我们反过来在我们的合约中要求它value_type是一个类似算术的类型。这又是一个我们编码的接口!)

然后,在设计您Filters显然打算用作 as 的产品Filters<FilterLike>时,您需要问自己 aFilterLike需要满足的通用最小接口是什么。如果有一些FilterX几乎FilterLike除了某些操作之外的,这里有一些选项:

  • 正如您在问题中提到的那样,无论在何处Filters使用该特定操作,您都可以对其进行特殊处理,以便对其FilterX进行特殊处理——这可能是您能做的最糟糕的事情。这很脆弱,因为您必须在需要操作的每个站点都执行此操作(即使现在看起来只有一个,将来呢?);不方便的是,您不能在类模板函数成员的函数体内打开类型(您必须使用冗长且不明显的不同技术);它引入了Filters必须知道的耦合FilterX——它为什么要关心?

  • 为 .写一个明确的特化Filters<FilterX>。这很像上面的,但是当FilterX不只是一个或两个操作不同时,这是一个有趣的选择。与之前的解决方案不同,这并不那么脆弱,因为主模板保持不变,所有FilterX特定的东西都放在同一个位置。另一方面,如果一半的FilterX行为已经像 a Filter,那么这一定意味着要么有一半的代码Filters<FilterLike>重复,要么需要一些额外的工作来重构和之间的公共Filters<Filter>代码Filters<FilterX>。因此,耦合量会有所不同——如果主模板不必知道这个显式特化,那么这是一个不错的选择,您甚至不必将显式特化与主模板捆绑在一起

  • 编写一个接口AdaptedFilterX模型,FilterLike并将其所有操作转发给底层FilterX。如果你有几个FilterXFilterY几乎都是模型Filter但有一个共同的接口,你可以写一个AdaptedFilter<FilterX>——从某种意义上说,AdaptedFilter模板将 a 的模型“转换”FilterXLike为 a 的模型FilterLike

顺便说一句,如果您使用的是 C++11,您可以编写Filter接受任意参数来构造 a 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...
}
于 2012-12-01T19:12:25.797 回答