通过模板多重继承

Multiple inheritance via templates

用模板继承(线性)代替虚拟多重继承(钻石)是个好主意吗?例如,我有这个 class 图:

       IBase
    /          \
   /            \
IExtendedBase  BaseImpl
  \            /
   ExtendedImpl

我知道我可以用虚拟继承来实现它。但是我可以使用模板来使这个图表线性化吗?

class IBase
    {
    public:
        virtual std::string name() = 0;
    };

    template<typename T>
    class BaseImpl : public T
    {
    public:
        virtual std::string name() override
        {
            return "BaseCommonImpl";
        }
    };

    template<typename T>
    class IExtendedBase : public T
    {
    public:
        virtual std::string extended_name() = 0;
    };

    template<typename T>
    class ExtendedBaseImpl : public T
    {
    public:
        virtual std::string extended_name() override
        {
            return "ExtendedBaseImpl";
        }
    };

现在使用 typedef 我可以专门化 ExtendedBase

typedef IExtendedBase<BaseImpl<IBase>> _ExtendedBase;
typedef ExtendedBaseImpl<_ExtendedBase> _ExtendedBaseImpl;

哪种方法更好?虚拟继承还是模板继承?

这个问题是征求意见,所以它可能会被版主关闭。

在此之前,我的建议是支持避免多重继承的方法:

#include <iostream>
#include <string>

class IBase
{
public:
    virtual std::string name() = 0;
};


class IExtendedBase : public IBase
{
public:
    virtual std::string extended_name() = 0;
};


template<typename T>
class BaseImpl : public T
{
public:
    virtual std::string name() override
    {
        return "BaseCommonImpl";
    }
};

template<typename T>
class ExtendedBaseImpl : public T
{
    using inherited = T;
public:
    virtual std::string extended_name() override
    {
        return "ExtendedBaseImpl";
    }

    // optionally override name if you wish
    std::string name() override {
        return inherited::name() + "(extended)";
    }
};

typedef BaseImpl<IBase> Base;
typedef ExtendedBaseImpl<BaseImpl<IExtendedBase>> ExtendedBase;


using namespace std;

int main()
{

    Base a;
    a.name();
    cout << a.name() << endl;

    ExtendedBase b;
    cout << b.extended_name() << endl;
    cout << b.name() << endl;

}

虽然您可以使用这两种不同的方法(多重继承与模板)获得相似的结果,但在语义上存在重要差异。

很遗憾,您没有提供足够的信息来推荐 objective 选择。所以这里有一些注意事项:

多重继承方式

多重继承是为了强制执行有效separation of concerns

如果您的情况 ExtendedImpl is-a IExtendBase 并且同时是-aBaseImpl,但是两者的继承关系是独立的。

在某些情况下(例如转换),它会带来轻微的性能开销。

但它的优点是允许您的 ExtendedImpl 在可以使用其任何基础的地方使用。此外,它允许动态的、基于运行时的多态性(如果它的任何一个基类有一个虚拟成员函数)。

模板方法

模板用于通用编程。如果您的 ExtendedImpl 确实是通用的,并且 "bases" 是您的通用概念的更多参数,而不是进一步扩展的概念,则应优先使用此方法。

Template 会在此处采用性能稍好(单继承)的方法。但是您没有实现初始模式的原始概念。而且您没有动态多态性的灵活性。

如果类型之间的关系不是通用的,您可能会在此处引入不需要的依赖关系。例如这里,IExtendedBase 将继承自 BaseImpl。在许多情况下这可能没问题。但在其他情况下,这可能完全不自然,导致维护阶段出现持久的设计问题。

结论

现在由您决定哪种优势和不便最适合您的具体情况。如果需要,您可以编辑您的问题,更准确地说明上下文和您的意图,我会调整答案 因此。