简体   繁体   English

通过模板专门化继承

[英]Inheritance through template specialization

Recently I found a case where is was easier to do a template specialization instead of real inheritance. 最近我发现了一个更容易进行模板特化而不是真正继承的情况。 The derived classes just had to implement a pure virtual function and had no own members. 派生类只需实现一个纯虚函数,并且没有自己的成员。 It was something like: 它是这样的:

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

This can be written as specialized templates as: 这可以写成专门的模板:

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

Both examples give the same results, I guess the second one is faster as no virtual tables need to be evaluated (the methods getValue() may even be inlined in the second case). 两个示例都给出了相同的结果,我猜第二个示例更快,因为不需要计算虚拟表(方法getValue()甚至可以在第二种情况下内联)。

So my question is: What are the limits of using template specialization instead of inheritance? 所以我的问题是:使用模板专业化而不是继承有什么限制? Are there any side effects I haven't seen? 有没有我没见过的副作用? Any benefits of inheritance over template specialization? 继承优于模板专业化的任何好处? I know that I can't create new members and methods for specialized classes as I can do for derived. 我知道我不能为专门的类创建新的成员和方法,因为我可以为派生创建。 But can this be a general, more performant approach for such use cases where I only need to implement some type-specific code? 但对于这种用例,我只需要实现一些特定于类型的代码,这是一种通用的,更高效的方法吗?

Btw: Is there a name for this pattern? 顺便问一下:这个模式有名字吗?

Templates and Inheritance are not interchangeable. 模板和继承不可互换。

  • Templates express static polymorphism (ie polymorphism at compile-time) 模板表示静态多态 (即编译时的多态)

  • Inheritance allows runtime polymorphism : you can manipulate Base class pointers and expect the runtime to call the correct virtual function for you. 继承允许运行时多态 :您可以操作Base类指针并期望运行时为您调用正确的虚函数。

Whit your templates approach, what if you want to manipulate a container of Base<> objects (eg std::vector<Base<??>> ) and call calculate() on them ? 使用模板方法,如果你想操作Base<>对象的容器(例如std::vector<Base<??>> )并在它们上调用calculate() ,该怎么办? You can't. 你不能。

So although both inheritance and templates express interfaces and polymorphism, they really are different beasts: choosing one over the other depend on your context, and how your types are going to be used. 因此,尽管继承和模板都表达了接口和多态,但它们实际上是不同的动物:选择一个而不是依赖于您的上下文,以及如何使用类型。

Note: 注意:

Performance considerations should not alter this choice 性能考虑因素不应改变此选择

When using superclasses and subclasses, either DerivedA or DerivedB can be passed to a non-templated function or a method that takes a Base class instance. 使用超类和子类时,可以将DerivedA或DerivedB传递给非模板化函数或采用Base类实例的方法。

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

The major limitation of the template-based approach is that this is no longer possible. 基于模板的方法的主要限制是不再可能。 method() will also have to be a template: method()也必须是一个模板:

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

If method() is large, there's going to be quite a code bloat, here. 如果method()很大,那么这里会有相当大的代码膨胀。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM