泛型/多态迭代器

Generic/polymorphic iterators

本文关键字:迭代器 多态 泛型      更新时间:2023-10-16

实现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();
}

请抵制"明智"的回应,如"不要使用全局变量"等。我只想知道是否有办法"概括"集合和矢量迭代器,这个例子只是为了让事情变得简单。

干杯

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

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也是一个很好的示例。

简短的回答是你不能。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成员初始化为return_value::vectorreturn_value::set,以便getIterator()的调用方可以检查返回值,并确定返回的迭代器类型。

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

你可以包装它,并使用多态性。下面是一个示例:

#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;
}

假设你真的想写

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

。你想让一个迭代器有条件地迭代其中一个序列,我的第一反应是:"不要这样做!首先,我发现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 一起使用。我还没有测量这个实现的性能(还没有?(,但我完全期望它 - 呃 - 不是很好。