【问题标题】:Using Polymorphism with Templates对模板使用多态性
【发布时间】:2012-12-01 17:57:00
【问题描述】:

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

例如,我有一个名为“过滤器”的类,我有许多不同的数据过滤方式/类,因此我根据模板初始化一个对象(在 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);
}

这不起作用,因为“过滤器”中的“过滤器”只接受 1 个参数。

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

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

任何想法都将不胜感激。

【问题讨论】:

  • Filter 可以接受 T 代替(因为这毕竟是它所需要的),但这可能只是因为你写了一个简单的例子。
  • @LucDanton 我应该更新我的帖子并包含一个更复杂的深入示例吗?

标签: c++ templates oop


【解决方案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;
}

(这个例子当然是假的,这里的目的是展示多个随机访问迭代器操作。)

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

  • 成员类型std::iterator_traits&lt;It&gt;::value_typestd::iterator_traits&lt;It&gt;::difference_type,分别是可以从operator[]operator-的结果初始化的类型
  • It 上的操作,例如 operator-operator[],它们可用于例如双向迭代器

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

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

然后在设计您显然打算用作Filters&lt;FilterLike&gt;Filters 时,您需要问自己FilterLike 需要满足的通用最小接口是什么。如果有一些FilterX,其中几乎FilterLike,除了一些操作,这里有一些选项:

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

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

    李>
  • 编写一个AdaptedFilterX,它是FilterLike 接口的模型,并将其所有操作转发给底层FilterX。如果您有几个FilterXFilterY,它们几乎都是Filter 的模型,但有一个共同的接口,您可以编写一个AdaptedFilter&lt;FilterX&gt;——在某种意义上,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...
}

【讨论】:

    【解决方案2】:

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

    希望这会有所帮助:)

    【讨论】:

    • 感谢您的回复。我的意思是 .. 过滤器有:Filter(vector &data) WHEREAS Filter1 是:Filter1(vector &data, double value);等等。如果我打电话给 Filtersfilter1(DATA, theType);它会抛出一个错误
    猜你喜欢
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 2021-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-09
    • 1970-01-01
    相关资源
    最近更新 更多