继承抽象迭代器接口

时间:2018-02-04 14:53:59

标签: c++

我想继承容器类的迭代器。它应始终具有与实际实现无关的相同签名。

但我遇到的问题是ContainerImpl Container::Iterator从[{1}}返回的ContainerImpl::begin()将被覆盖的方法从ContainerImpl::Iterator切换回Container::Iterator的方法。

如何强制使用更具体的方法? 或者我可以重写我的类,使其表现得像那样吗?

以下是一个工作示例。它至少可以编译成C ++ 17。

#include <tuple>

using std::tuple;

class Container {
    // some code

public:

    class Iterator {
    public:
        virtual tuple<int, int> &operator*() {};

        virtual Iterator &operator++() {};

        virtual bool operator!=(const Iterator &rhs) const {};
    };

    virtual Iterator begin() = 0;

    virtual Iterator end() = 0;
};

class ContainerImpl : public Container {
    // some code

public:

    class Iterator : public Container::Iterator {
    public:
        Iterator(ContainerImpl &containerImpl) {
            // init
        }

        Iterator() {
            // end_iterator
        }

        virtual tuple<int, int> &operator*() override {
            // impl
        };

        virtual Container::Iterator &operator++() override {
            // impl
        };

        virtual bool operator!=(const Container::Iterator &rhs) const override {
            // impl
        };
    };

    virtual Container::Iterator begin() override {
        return Iterator{*this};
    };

    virtual Container::Iterator end() override {
        return Iterator{};
    };
};


int main() {
    ContainerImpl cont{};
    for (auto &entry : cont) {
        // here Container::Iterator::operator++() is called
        // instead of ContainerImpl::Iterator::operator++()
    }
}

编辑:

谢谢!我读完了你的评论并发现我应该尝试使用模板的静态方法。性能对于那些迭代器来说非常重要。这将需要重构我使用此代码的其他代码。但它应该是温和的。

以下是我提出的建议:

#include <tuple>
#include <type_traits>

using std::tuple;

template<class SubContainer>
class Container;


template<class ContImpl>
class Iterator {
    static_assert(std::is_base_of<Container<ContImpl>, ContImpl>::value, "ContImpl not derived from Container");
public:

    tuple<int, int> &operator*();

    Iterator<ContImpl> &operator++();

    bool operator!=(const Iterator<ContImpl> &rhs) const;
};



template<class SubContainer>
class Container {
public:
    Iterator<SubContainer> begin();

    Iterator<SubContainer> end();

};

class ContImpl;

template<>
class Iterator<ContImpl> {
public:
    ContImpl &container;

    Iterator(ContImpl &container, bool is_end = false) : container(container) { init(); }

    void init() {
    }

    tuple<int, int> &operator*() {
    }

    Iterator<ContImpl> &operator++() {
    }

    bool operator!=(const Iterator<ContImpl> &rhs) const {
    }
};

class ContImpl : public Container<ContImpl> {
public:
    Iterator<ContImpl> begin() {
        return Iterator<ContImpl>(*this, false);
    }

    Iterator<ContImpl> end() {
        return Iterator<ContImpl>(*this, true);
    }
};


int main() {
    ContImpl cont{};
    for (auto &entry : cont) {
        // Iterator<ContImpl>::operator++() is called.
    }
}

1 个答案:

答案 0 :(得分:0)

如果你想动态地抽象出std::list<int>std::vector<int>之间的差异,那么你想要的是type erasure。但请注意,使用模板模板参数之类的东西,也可以静态抽象具有相似接口(具有相同或不同元素类型)的容器,这些(如果它符合您的要求)应该要快得多。

关于基于模板的方法:虽然它可能具有文档的价值,但Container中的方法是不必要的:静态多态性不需要任何“基本函数”来覆盖。如果删除它们,则可以完全不使用该CRTP类。然后,类似地,您可以使用简单的类Iterator替换ContImpl::iterator类模板。

当然,此时剩下的就是标准容器之间的接口兼容性相同(允许代码如

template<template<class> class C>
C<int> makeInts(/*...*/);

void f() {
  auto d=makeInts<std::deque>(/*...*/);
  // ...
}

我在上面建议)。有趣的练习是隐藏包装类后面的不同的容器接口或者使用traits接口。