将继承转换为模板

convert inheritance to templates

我有一个使用继承的 C++11 项目。这是小片段:

class ICountable{
public:
        virtual ~ICountable(){}

        unsigned getCount() const{
                return _getCount();
        }

        bool isEmpty() const{
                return _getCount() == 0;
        }

private:
        virtual unsigned _getCount() const = 0;
};

假设我们有一些 LinkList 继承自 ICountable 并实现 _getCount()。然后你可以像这样制作函数:

void doSomething(ICountable &countable){
    if (countable.isEmpty())
         doSomethingElse();
}
...
LinkList ll;
doSomething(ll);

这一切都很好,但必须有另一种方法来做到这一切:

template <typename T>
void doSomething(T countable){
    if (countable.isEmpty())
         doSomethingElse();
}
...
LinkList ll;
doSomething(ll); // we do not even need to add <>

模板方式更快而且可能更容易实现。 std::vectorstd::deque也是这样。

但是,如何避免代码重复 - 函数 isEmpty() 必须粘贴在所有 "list things".

我可以想象预处理器 #include 或...

我可以想象 decorator-like class 可以实现所有这些糖方法并代理其他方法:

template <typename T>
class List{
   T list;
public:
        unsigned getCount() const{
                return list.getCount();
        }

        bool isEmpty() const{
                return list.getCount() == 0;
        }

        ...
}

如果没有运行时多态性,继承或模板哪个更好?

有没有更好的方法来避免代码重复?

使用模板,您只需隐含接口即可。您编写一个函数模板,并要求您传入的类型符合您的条件。在这种情况下,只需:

template <typename T>
void doSomething(T const& countable) {
    if (countable.empty()) {
         doSomethingElse();
    }
}

所有 C++ 标准容器都有一个 empty() 方法,您可能比复制自己编写的容器的标准接口做得更糟。此函数已经适用于 vectorlist 以及 string 和 ...,所有这些都没有任何动态调度开销,也没有必须从某些接口继承的 OOP 要求。

如果类型具有您需要的接口,泛型编程就可以工作——它们不需要继承所有正确命名的接口来获取它们。

有一个 C++ 特性尚未正式添加到标准中(但已经存在了很长一段时间)首先称为 Concepts that would essentially let you declare your doSomething(T countable) to have the requirement that T implement a certain interface (ie, that it has an isEmpty() function). Until then, you can't avoid polymorphism if what you want is to avoid code duplication by sharing a set of common functions (re: interface) between different classes. That's one of the main reasons to use polymorphism

话虽如此,您正在寻找像评论中提到的 Igor Tandetnik 这样的 Curiously Recurring Template Pattern。这允许您在 class 和 之间共享公共代码,而不会 虚拟函数的运行时损失(运行时多态性)。使用 CRTP 可以让编译器为您做这些事情。

使用简单的基础class:

template <typename T>
struct ICountable {
    bool isEmpty() const { return static_cast<T*>(this)->_getCount() == 0; }
};

然后是你的导出列表:

class LinkedList : public ICountable<LinkedList> {
    int _getCount() const { return size; }
};

以这种方式从 ICountable 派生的任何 class 现在将有一个 isEmpty() 方法,该方法使用特定于实现的 _getCount() 方法,而不使用虚函数.