编译时纯虚函数

时间:2015-06-20 09:50:11

标签: c++ templates

我尝试过实施一个列表容器, 并决定移动一些一般功能 像sum()一样基类,所以我可以 稍后在其他容器中重复使用它们。

所有基本支持类需要三个 方法empty()head()tail。 因为支持,我无法制作那些纯粹的虚拟虚拟内容 class永远不会被实例化。但它仍然存在 必须使用这些方法来实现自己的方法 像sum()这样的方法。

我试过这样的事情:

#include <iostream>
using namespace std;

template<typename T>
class StatsSupport {
public:
    T sum(void) const {
        if (empty()) {
            return T(0);
        } else {
            return head() + tail()->sum;
        }
    }

    // other methods
};

template<typename T>
class List : public StatsSupport<T> {
public:
    // constructors etc.

    bool empty(void) const {return head_ != NULL;}
    const T& head(void) const {return *head_;}
    const List<T>& tail(void) const {return *tail_;}

    // other methods
private:
    T* head_;
    List<T> *tail_;
};

但是尝试使用sum()会让我出现编译错误

prog.cpp:8:13: error: there are no arguments to 'empty' that depend on a template parameter, so a declaration of 'empty' must be available [-fpermissive]
   if (empty()) {
             ^

代表empty()head()tail()

有什么建议吗?

3 个答案:

答案 0 :(得分:1)

问题是StatsSupport无法找到emptyhead等函数,因为它们既不存在于全局范围内,也不存在于全局范围内。 StatsSupport不知道派生类中存在的函数。

基本上有两种方法可以解决这个问题:

  • 运行时多态,在其中向StatsSupport添加虚拟析构函数,并为emptyhead等添加纯虚拟的声明。
  • 使用评论中提到的CRTP编译时间多态性。 我会专注于后者。

所以基本上StatsSupport需要一种方法来访问派生类的函数。 这可以通过添加派生类的类型作为模板参数来完成,该参数称为CRTP

template<class Derived, typename T>
class StatsSupport {
public:
    T sum(void) const {
        if (derived()->empty()) {
            return T(0);
        } else {
            return derived()->head() + derived()->tail()->sum;
        }
    }

    // other methods
    private:
        Derived *derived()
        {
            return static_cast<Derived*>(this);
        }
        const Derived *derived() const
        {
            return static_cast<const Derived*>(this);
        }
};

template<typename T>
class List : public StatsSupport<List<T>, T> { // with some changes could be simplified to StatsSupport<List<T>> but this it ouf of scope of this question

我正在使用derived的函数而不是成员来保持类const正确。

当然,另一种选择是依赖于算法的不同设计。在那里,您将sumStatsSupport的所有其他功能移动到全局namesapce中,然后像sum(my_container_instance)一样访问它们。 更像STL的方式是使用迭代器。然后你可以使用std::accumulate进行求和。

答案 1 :(得分:0)

这是一个严重的设计问题:您的StatSupport定义了一些常规函数,但依赖于其子类的细节。

因此,当编译StatSupport时,它甚至不知道有head()tail()。这就是您收到错误消息的原因

现在想象有一天你想要定义其他容器来继承自StatSupport,例如你自己的Vector或Map,或DataBase。这些数据结构不会有头尾。

基本上你可以采取两种主要方向:

  • 在StatSupport中定义一些用于迭代数据结构的虚函数。
  • 或者更好,在数据结构中使用一些迭代器(就像标准容器一样)并定义一些使用迭代器浏览容器的模板函数(sum,average等等)。

在后一种情况下,您不需要继承来从泛型函数中受益。

答案 2 :(得分:0)

我可能会错过问题的重点,但无论如何都会给我5美分:)

我在下面展示的解决方案背后的原因是,OOP新手(在C ++中)通常认为他们必须使用继承来完成任务。

但特别是在C ++中,这只是一种方式,而且往往不是实现构图的最佳方式。

虽然在大多数情况下,虚函数的开销成本并不重要,但下面的代码显示了一种在不使用继承和不使用虚函数的情况下产生容器扩展的方法。方法的弱点是“容器函数契约”只是隐含可见。

template <class _X>
class ContainerTypeA < _X >
{
public:
    typedef _X value_type;
    typedef ContainerTypeA<_X> container_type;

    const _X & Head() const
    {
        // return head of this containers content.
    }
    container_type Tail() const
    {
        // return the tail (all elements after the first element in a new instance.
    }
    bool IsEmpty() const
    {
        return true; // return whether or not this container is empty.
    }
};

template <class _X>
class ContainerTypeB < _X >
{
public:
    typedef _X value_type;
    typedef ContainerTypeB<_X> container_type;

    const _X & Head() const
    {
        // return head of this containers content.
    }
    container_type Tail() const
    {
        // return the tail (all elements after the first element) in a new instance.
    }
    bool IsEmpty() const
    {
        return true; // return whether or not this container is empty.
    }
};

// Note: In stead of the class with static member functions, this could 
// as well be a namespace with template-functions inside.
template < class _ContainerT >
class ContainerStats<_ContainerT>
{
    static _ContainerT::value_type Sum(const _ContainerT & container)
    {
        // Implement sum - possibly in the recursive way you did in your question.
    }
    // more expansion functions...
};