C++:“可迭代<T>"界面 [英] C++: "Iterable<T>" interface

查看:28
本文介绍了C++:“可迭代<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> 的实现:

And based on that here's an implementation of an 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++:“可迭代&lt;T&gt;"界面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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