泛型/多态迭代器

时间:2014-12-19 01:44:26

标签: c++ stl iterator containers

实现getIterator的最佳方法是什么?根据条件,我想返回相应的迭代器。

// global variables
vector<int> myVector;
set<int> mySet;

vector<int>/set<int>::iterator getIterator(bool someCondition) {
    if (someCondition) return mySet.begin();
    else return myVector.begin();
}

请拒绝“不要使用全局变量”等“明智”的回答。我只想知道是否有一种方法可以“概括”set和vector迭代器,这个例子只是为了简单起见。

干杯

4 个答案:

答案 0 :(得分:2)

是的,迭代器可以泛化,但您可能需要编写一个包装类。实现它有几种选择。显然,你需要在类中存储一个实际的迭代器,并有办法确定迭代器是什么。例如:

class MyIter {
...
private:
    union {
        std::vector<int>::iterator vec_iter;
        std::set<int>::iterator set_iter;
    }
    bool is_vec_iter; /* or like in an example above, enum { vector, set} type_t; */
};

应该很明显,如何构造这样的类的对象。有趣的部分是实现接口,即解除引用,递增,比较迭代器。

看看boost :: iterator_facade:http://www.boost.org/doc/libs/1_53_0/libs/iterator/doc/iterator_facade.html可能是件好事。它是一个帮助程序模板,它只使用几个必须提供的解除引用和遍历方法来实现大多数迭代器操作。即使您决定从头开始实现所有内容,iterator_facade也是一个很好的例子。

答案 1 :(得分:1)

简短的回答是,你不能。 C ++是一种静态类型的语言。这意味着函数的类型或方法的返回值是在编译时声明的,而不是在运行时声明的。

其他语言(例如Perl)是动态类型的。 Perl函数有时可以返回标量值,如整数。其他时候它可以返回引用(指针),列表或散列(std :: vector或std :: map)。但是C ++不能这样工作。

因此,如果您需要编写动态类型的代码,则需要使用除C ++之外的其他语言。

在C ++中你唯一能做的就是将这个函数声明为返回一些可以转换为任何一种类型的函数。

例如:

class return_value {

public:

    enum { vector, set} type_t;

    type_t type;

    std::vector<int>::iterator v_iter;
    std::set<int>::iterator s_iter;
};

return_value getIterator(bool someCondition) {
    // ...
}

然后,您的getIterator()函数会构建一个return_value实例,初始化其v_iters_iter成员,并将其type成员初始化为{{} 1}}或return_value::vector,以便return_value::set的调用者可以检查返回值,并确定返回什么类型的迭代器。

不同的方法也是可能的。例如,如果可以根据getIterator()的参数确定返回值的类型,则可以使用模板和专门化来实现静态类型的解决方案。

答案 2 :(得分:1)

你可以包装它,并使用多态。这是一个例子:

#include <memory>
#include <vector>
#include <set>
#include <iostream>

using namespace std;

class GenericIterator_helper_base {
        friend class GenericIterator;
    public:
        virtual ~GenericIterator_helper_base() = default;
        virtual int operator*() = 0;
        virtual void pre_inc() = 0;
};

template <typename IT>
class GenericIterator_helper_tmpl : public GenericIterator_helper_base {
    public:
        GenericIterator_helper_tmpl(IT &&it_) : it(it_) { }
        virtual ~GenericIterator_helper_tmpl() = default;
        virtual int operator*() { return *it; }
        virtual void pre_inc() { ++it; }
    private:
        IT it;
};

class GenericIterator {
    public:
        template <typename T>
        GenericIterator(T &&it) : helper(new GenericIterator_helper_tmpl<T>(std::move(it))) { }
        int operator*() { return helper->operator*(); }
        GenericIterator &operator++() { helper->pre_inc(); return *this; }
    private:
        std::unique_ptr<GenericIterator_helper_base> helper;
};

vector<int> myVector{1, 2};
set<int> mySet{3, 4};

GenericIterator
getIterator(bool cond) {
    if (cond) {
        return GenericIterator(mySet.begin());
    } else {
        return GenericIterator(myVector.begin());
    }
}

int main() {
    auto it1 = getIterator(true);
    auto it2 = getIterator(false);

    cout << *it1 << endl;
    ++it1;
    cout << *it1 << endl;

    cout << *it2 << endl;
    ++it2;
    cout << *it2 << endl;
}

答案 3 :(得分:1)

假设你真的打算写

std::vector<int> myVector;
std::set<int>    mySet;

...并且你希望得到一个迭代器有条件地迭代其中一个序列,我的立即反应是:&#34;不要这样做!&#34;首先,我发现std::set<T>对任何事情都很有用,极少数情况下它可能有用,std::unordered_set<T>是一个更好的选择。但是,这有点像切线。

更重要的是:编程的全部要点是使事情变得快速,而对低级操作的运行时决策必然会干扰性能。您重新设计系统以避免使用std::set<T>std::unordered_set<T>并始终使用std::vector<T>,这样做会更好。如有必要,对必须表现得像集合的实例使用适当的类似集合的操作。

好的,还在看吗?没有:我对上面的内容很认真。继续前行考虑吧!在不同的容器上使用迭代器并不是一个很好的方法,至少不是STL迭代器。 STL迭代器意味着速度快,因此它们为每个基本操作(提前,比较,访问)使用单独的低级操作。如果您坚持使用该接口,则会产生性能问题:使用动态多态方法的接口使用Enumerable接口(或类似的接口)将所有三个操作折叠为一个接口是有原因的:安全的动态调度! (所以,你真的应该考虑不做下面的概述)

好的,还在读书,也就是说,你把自己画成了一个角落。好吧,这里有足够的绳索来悬挂性能但可能会让你走出这个紧张的地方:使用C ++ 11你可以使用包含类类型的union。您只需要确保在施工和销毁期间妥善处理它们。您可以使用它来分派到合适的动态接口,而无需分配内存:

#include <iostream>
#include <iterator>
#include <algorithm>
#include <set>
#include <vector>
#include <new>


namespace demo {
    template <typename It0, typename It1>
    class joint_iterator {
    public:
        typedef typename std::iterator_traits<It0>::value_type value_type;
        typedef typename std::input_iterator_tag iterator_category;
        typedef std::ptrdiff_t difference_type;
        typedef value_type* pointer;
        typedef value_type& reference;

    private:
        struct dyn_base {
            dyn_base() = default;
            dyn_base(dyn_base const&) = default;
            virtual ~dyn_base() {}
            virtual bool equal(dyn_base const* other) const = 0;
            virtual void increment() = 0;
            virtual value_type access() = 0;
            virtual int index() const = 0;
        };
        template <typename It, int Index>
        struct dyn_it: dyn_base {
            dyn_it(It it): it(it) {}
            It it;
            bool equal(dyn_base const* other) const override {
                return this->it == static_cast<dyn_it<It, Index> const*>(other)->it;
            }
            void increment() override { ++this->it; }
            value_type access() override { return *this->it; }
            int index() const override { return Index; }
        };
        union it_rep{
            it_rep() {}
            ~it_rep() {}
            int         dummy;
            dyn_it<It0, 0> it0;
            dyn_it<It1, 1> it1;
        } rep;
        dyn_base* it;

    public:
        ~joint_iterator() { this->it->~dyn_base(); }
        joint_iterator(joint_iterator const& other) {
            if (other.it->index() == 0) {
                new(&this->rep.it0) dyn_it<It0, 0>(other.rep.it0); it = &this->rep.it0;
            }
            else {
                new(&this->rep.it1) dyn_it<It1, 1>(other.rep.it1); it = &this->rep.it1;
            }
        }
        joint_iterator& operator=(joint_iterator const& other);
        joint_iterator(It0 it0) { new(&this->rep.it0) dyn_it<It0, 0>(it0); it = &this->rep.it0; }
        joint_iterator(It1 it1) { new(&this->rep.it1) dyn_it<It1, 1>(it1); it = &this->rep.it1; }

        bool operator== (joint_iterator const& it) const {
            return this->it->equal(it.it);
        }
        bool operator!= (joint_iterator const& it) const {
            return !(*this == it);
        }
        value_type operator*() const { return this->it->access(); }
        joint_iterator& operator++() { this->it->increment(); return *this; }
        joint_iterator operator++(int) { joint_iterator rc(*this); this->operator++(); return rc; }
    };
}

int main(int ac, char*[])
{
    std::set<int>    s{ 11, 12, 13, 14, 15, 16, 17, 18, 19 };
    std::vector<int> v{ 21, 22, 23, 24, 25, 26, 27, 28, 29 };

    typedef demo::joint_iterator<std::set<int>::iterator, std::vector<int>::iterator> joint_iterator;
    std::copy(ac == 2? joint_iterator(s.begin()): joint_iterator(v.begin()),
              ac == 2? joint_iterator(s.end()): joint_iterator(v.end()),
              std::ostream_iterator<int>(std::cout, " "));
    std::cout << '\n';
}

此代码缺少某些方法实现(例如,缺少赋值运算符),并且在计算类型方面采取了一些快捷方式。它没有经过彻底的测试,但至少可以与最近的gcc和clang一起使用。我还没有测量过这个实现的性能(但是?)但是我完全希望它 - 它不是很好。