【问题标题】:Inheritance through template specialization通过模板特化继承
【发布时间】:2014-08-17 16:34:48
【问题描述】:

最近我发现了一个更容易进行模板特化而不是真正继承的情况。派生类只需要实现一个纯虚函数并且没有自己的成员。是这样的:

#include <iostream>

class Interface {
public:
    virtual void calculate() = 0;
    virtual float getResult() = 0;
};

class Base : public Interface {
    float result;
public:
    Base() : result(1) {};
    virtual ~Base() {};

    virtual void calculate();
    virtual float getValue() = 0; // do some very complex calculation here

    float getResult() { return result; }
};

class DerivedA : public Base {
public:
    DerivedA() : Base() {};
    ~DerivedA() {};

    float getValue();
};

class DerivedB : public Base {
public:
    DerivedB() : Base() {};
    ~DerivedB() {};

    float getValue();
};

void Base::calculate() {
    for (int i = 0; i < 10; i++)
        result += getValue();
}

float DerivedA::getValue() {
    return 1;
}

float DerivedB::getValue() {
    return 1.1;
}

int main() {
    Interface * a = new DerivedA();
    a->calculate();

    Interface * b = new DerivedB();
    b->calculate();

    std::cout << "Result A: " << a->getResult() << std::endl;
    std::cout << "Result B: " << b->getResult() << std::endl;

    delete a;
    delete b;
}

这可以写成专门的模板:

#include <iostream>

class Interface {
public:
    virtual void calculate() = 0;
    virtual float getResult() = 0;
};

template<typename T>
class Base : public Interface {
    float result;
public:
    Base() : result(1) {};

    void calculate();
    float getValue(); // do some very complex calculation here

    float getResult() { return result; };
};

typedef Base<int>   DerivedA; // actually int and float are only examples
typedef Base<float> DerivedB; // and may be some much more complex types!

template<typename T>
void Base<T>::calculate() {
    for (int i = 0; i < 10; i++)
        result += getValue();
}

template<typename T>
float Base<T>::getValue() {
    return 0;
}

template<>
float Base<int>::getValue() {
    return 1;
}

template<>
float Base<float>::getValue() {
    return 1.1;
}

int main() {
    Interface * a = new DerivedA();
    a->calculate();

    Interface * b = new DerivedB();
    b->calculate();

    std::cout << "Result A: " << a->getResult() << std::endl;
    std::cout << "Result B: " << b->getResult() << std::endl;

    delete a;
    delete b;
}

两个示例给出相同的结果,我猜第二个更快,因为不需要评估虚拟表(getValue() 方法甚至可以在第二种情况下内联)。

所以我的问题是:使用模板专业化而不是继承有什么限制?有没有我没见过的副作用?继承比模板专业化有什么好处?我知道我不能像为派生类创建新成员和方法那样为专门的类创建新成员和方法。但是对于我只需要实现一些特定于类型的代码的用例来说,这是否可以是一种通用的、性能更高的方法?

顺便说一句:这种模式有名字吗?

【问题讨论】:

  • 如果你以后需要DerviedC,而getValue应该返回4.2,你会怎么做?完全不清楚练习的目的是什么。据我所知,整个 shebang 可以用一个独立的函数 float calculate(float) 替换。
  • 本例中用于模板特化的类型只是示例。就我而言,我还有一些其他类型。更准确地说:我必须序列化更复杂类型的列表。但是 getValue() 的作用在这里并不重要。无论如何,这将是两种方法之间的共同部分。
  • 如所写,我不明白您要做什么。你的例子对我来说没有多大意义。也许你可以想出一个更现实的场景,其中所有这些扭曲都是真正需要的。
  • 你在找CRTP吗?
  • policiesstrategies

标签: c++ templates inheritance


【解决方案1】:

模板和继承不可互换。

  • 模板表达静态多态性(即编译时的多态性)

  • 继承允许运行时多态性:您可以操作Base 类指针并期望运行时为您调用正确的虚函数。

使用您的模板方法,如果您想操作Base&lt;&gt; 对象的容器(例如std::vector&lt;Base&lt;??&gt;&gt;)并在它们上调用calculate() 怎么办?你不能。

因此,尽管继承和模板都表达了接口和多态性,但它们确实是不同的野兽:选择其中一个取决于您的上下文以及您的类型将如何被使用。

注意:

性能考虑不应改变此选择

【讨论】:

    【解决方案2】:

    使用超类和子类时,可以将 DerivedA 或 DerivedB 传递给非模板函数或采用基类实例的方法。

    void method(Base &base)
    {
        // ...
    }
    

    基于模板的方法的主要限制是这不再可能。 method() 也必须是一个模板:

    template<typename T>
    void method(Base<T> &base)
    {
        // ...
    }
    

    如果 method() 很大,这里的代码会很臃肿。

    【讨论】:

    • 我改进了我的示例。最初 Base 旨在从接口派生。对不起,我把它简化了。
    猜你喜欢
    • 2013-06-08
    • 1970-01-01
    • 2012-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-19
    相关资源
    最近更新 更多