C ++:"Iterable T"界面 [英] C++: "Iterable<T>" interface

查看:60
本文介绍了C ++:"Iterable T"界面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要实现的目标可能很容易解释:考虑一下,我有一个抽象类,我知道它将包含多个已知类型的对象.但是,包含这些对象的实际容器将在子类中实现.
现在,在我的抽象基类中,我想提供一个接口来迭代这些对象.考虑到我不知道(或者不想修复)容器的类型,我认为迭代器可能是我最好的选择.

What I want to achieve is probably easily explained: Consider I have an abstract class that I know will contain multiple objects of known type. However the actual container holding these objects will be implemented in sub-classes.
In my abstract base class I now want to provide an interface to iterate over these objects. Given that I don't know (or rather don't want to fix) the type of container, I thought that iterators would probably be my best bet.

此类的概念性声明可能如下所示:

A conceptual declaration of this class might look like this:

class MyClass {
public:
    // Other interface methods, e.g. size()

    virtual Iterable<MyObject> objects() = 0;
};

这里的意图是,我将能够像这样遍历类的嵌套对象:

The intention here is that I'll be able to iterate over the nested objects of my class like this:

MyClass *class = new ImplementationOfClass();
for (const MyObject &obj : class->objects()) {
    // Do stuff with obj
}

但是,我面临的问题是我似乎无法弄清楚应如何定义 Iterable< MyObject> .该对象的关键属性是,在定义此类时,我只能指定返回的值是可迭代的(使用STL样式的迭代器),并且在使用时将产生类型为 MyObject 的对象.迭代器已取消引用.

The issue I am facing however is that I can't seem to figure out how Iterable<MyObject> should be defined. The key property of this object is that at the time of defining this class I can only specify that the returned value will be iterable (using STL-style iterators) and will yield objects of type MyObject when the used iterator is dereferenced.

通常我会为此单独使用一个抽象类,但这似乎很棘手(不可能吗?),因为迭代器总是按值传递,因此据我所知,不可能有多态性.

Normally I would use an abstract class on its own for this but it seems that this is very tricky (impossible?) since iterators are always passed by value and thus to my knowledge no Polymorphism is possible.

关于如何将任意迭代器类型作为参数传递给函数的问题总是带有使用模板".回答.但是我认为我不能为此使用模板.不过,这种假设可能是错误的,请随时纠正我.

Questions dealing with how to pass arbitrary iterator types as arguments into a function always come up with the "use templates" answer. However I think in my case I can't use templates for that. This assumption might be wrong though, so feel free to correct me.

本质上,我经常遇到的障碍是,在某些时候,我必须明确写下迭代器类型,在我的情况下,我不能这样做.我考虑过为此使用模板,但这会抑制适当的多态性(我认为吗?),因为该抽象接口的用户似乎有显式初始化正确模板的负担.但是,所有这些的全部要点是,调用方不必关心底层结构.

Essentially the barrier I always run into is that at some point I have to write down the iterator type explicitly which in my case I can't. I thought about using a template for that but this would then inhibit proper Polymorphism (I think?) because the user of that abstract interface seems to have the burden of explicitly initializing the correct template. The whole point of all of this however is that the caller does not have to care about the underlying structure.

TL; DR:有没有办法创建一个仅允许可迭代的接口类,并且取消引用迭代器将产生类型T的对象?

TL;DR: Is there a way to create an interface class that only promises to be iterable and that dereferencing an iterator will yield an object of type T?

推荐答案

在@FrançoisAndrieux的帮助下,以及 https://stackoverflow.com/a/4247445/3907364 ,我能够提出解决问题的方法.

With the help of @FrançoisAndrieux and a hint from https://stackoverflow.com/a/4247445/3907364, I was able to come up with an approach to my problem.

本质上,这个想法是创建一个迭代器包装器,该包装器存储一个函数,以在给定索引的情况下获得给定类型的对象.那个索引就是迭代的对象.

Essentially the idea is to create an iterator-wrapper that stores a function to obtain an object of the given type if given an index. That index is then what is iterated on.

对此的好处是,迭代器接口是通过指定取消引用的对象的类型来修复的.通过使成员函数 objects() virtual 来发挥多态性,以便每个子类都可以构造迭代器本身,并提供自定义函数指针.因此,只要有一种方法可以将索引映射到容器中的各个元素(无论使用哪种方法),此技巧就可以使用.

The nice thing about this is that the iterator interface is fixed by specifying the type of object that dereferencing it should return. The polymorphism comes into play by making the member function objects() virtual so that each sub-class can construct the iterator itself, providing a custom function pointer. Thus as long as there is a way to map an index to the respective element in the container (whichever is used), this trick is usable.

请注意,您可以直接使用指向 std :: vector< T> :: at 的指针,也可以创建一个将返回相应元素的自定义函数.

Note that you can either directly use pointers to e.g.std::vector<T>::at or create a custom function that will return the respective element.

这是迭代器的实现(实现可能会有所改善,但似乎可以完成工作):

Here's the implementation for the iterator (The implementation could probably be improved upon but it seems to get the job done):

template< typename T > struct iterator_impl {
    using iterator_category = std::forward_iterator_tag;
    using difference_type   = std::ptrdiff_t;
    using value_type        = T;
    using pointer           = T *;
    using reference         = T &;

    using access_function_t = std::function< T &(std::size_t) >;

    // regular Ctor
    iterator_impl(std::size_t start, access_function_t &func, const void *id)
        : m_index(start), m_func(func), m_id(id) {}

    // function-move Ctor
    iterator_impl(std::size_t start, access_function_t &&func, const void *id)
        : m_index(start), m_func(func), m_id(id) {}

    // copy Ctor
    iterator_impl(const iterator_impl &) = default;

    // move ctor
    iterator_impl(iterator_impl &&other) {
        std::swap(m_index, other.m_index);
        m_func = std::move(other.m_func);
        std::swap(m_id, other.m_id);
    }

    // copy-assignment
    iterator_impl &operator=(const iterator_impl &other) = default;

    // prefix-increment
    iterator_impl &operator++() {
        ++m_index;
        return *this;
    }

    // postfix-increment
    iterator_impl operator++(int) {
        iterator_impl old = *this;
        ++(*this);
        return old;
    }

    bool operator==(const iterator_impl &other) { return m_index == other.m_index && m_id == other.m_id; }

    bool operator!=(const iterator_impl &other) { return !(*this == other); }

    T &operator*() { return m_func(m_index); }

    T *operator->() { return &m_func(m_index); };

protected:
    std::size_t m_index = 0;
    access_function_t m_func;
    const void *m_id = nullptr;
};

请注意,我必须引入 m_id 成员变量,以正确比较迭代器( std :: function 不能使用 ==进行比较).它意味着例如元素所在的容器的地址.其唯一目的是确保恰好具有相同索引但在完全不同的集合上进行迭代的2个迭代器不被视为相等.

Note that I had to introduce the m_id member variable as a means to properly compare iterators (std::function can't be compared using ==). it is meant to be e.g. the address of the container the elements are contained in. Its sole purpose is to make sure that 2 iterators that happen to have the same index but are iterating over completely different sets are not considered equal.

基于此,这是 Iterable< T> 的实现:

template< typename T > struct Iterable {
    using iterator       = iterator_impl< T >;
    using const_iterator = iterator_impl< const std::remove_const_t< T > >;

    Iterable(std::size_t start, std::size_t end, typename iterator_impl< T >::access_function_t &func, const void *id)
        : m_begin(start, func, id), m_end(end, func, id) {}

    iterator begin() { return m_begin; }
    iterator end() { return m_end; }

    const_iterator begin() const { return m_begin; }
    const_iterator end() const { return m_end; }

    const_iterator cbegin() const { return m_begin; }
    const_iterator cend() const { return m_end; }

protected:
    iterator m_begin;
    iterator m_end;
};

这篇关于C ++:"Iterable T"界面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆