为什么没有“Iterable” STL 中的接口?

发布于 2024-09-27 18:14:04 字数 1055 浏览 0 评论 0原文

C++ STL 似乎并不经常使用纯抽象基类(也称为接口)。我知道大多数事情都可以通过 STL 算法或巧妙的模板元编程来实现。

,对于某些用例(例如,在 API 中,如果我不想具体说明我获得的容器的类型,而只想具体说明它包含的元素),以下形式的接口会很好:

template<typename T> struct forward_iterable {
    struct iterator {
        typedef T  value_type;
        typedef T& reference;
        typedef T* pointer;
        virtual reference operator*() const = 0;
        virtual pointer operator->() const = 0;
        virtual bool operator==(const iterator&) const = 0;
        virtual bool operator!=(const iterator&) const = 0;
        virtual operator const_iterator() const = 0;
        virtual iterator& operator++() = 0;
        virtual iterator  operator++(int) = 0;
    };
    struct const_iterator { ... };  // similar, but with const references

    virtual iterator begin() = 0;
    virtual const_iterator begin() const = 0;
    virtual iterator end() = 0;
    virtual const_iterator end() const = 0;
};

但是 STL 容器将此类实现为非虚拟函数,在我看来,这不会影响性能(如果我直接使用容器而不是通过此接口)。那么为什么STL中的“接口”这么少呢?或者我只是用“Java”术语想得太多了?

The C++ STL does not seem to use purely abstract base classes (aka interfaces) very often. I know that most things can be achieved with the STL algorithms or clever template metaprogramming.

But still, for some use cases (for example, in an API, if I do not want to be specific about the type of container I get, just about the elements it contains), an interface of the following form would be nice:

template<typename T> struct forward_iterable {
    struct iterator {
        typedef T  value_type;
        typedef T& reference;
        typedef T* pointer;
        virtual reference operator*() const = 0;
        virtual pointer operator->() const = 0;
        virtual bool operator==(const iterator&) const = 0;
        virtual bool operator!=(const iterator&) const = 0;
        virtual operator const_iterator() const = 0;
        virtual iterator& operator++() = 0;
        virtual iterator  operator++(int) = 0;
    };
    struct const_iterator { ... };  // similar, but with const references

    virtual iterator begin() = 0;
    virtual const_iterator begin() const = 0;
    virtual iterator end() = 0;
    virtual const_iterator end() const = 0;
};

If the STL containers implement this class as non-virtual function, this would, in my opinion, not affect performance (if I use the containers directly and not via this interface). So why are there so few "interfaces" in the STL? Or am I just thinking too much in "Java" terms?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

烟若柳尘 2024-10-04 18:14:04

STL(标准库的子集)根本不使用 OOP(如运行时多态性),这是设计使然。

根据您的设计,按值返回迭代器不会出现问题(协方差不适用于值类型)吗?也就是说,整个事情是否不可避免地必须依赖静态成员(可以通过引用返回)或堆分配的迭代器?后者在非垃圾收集语言中显得相当尴尬。

您所描述的(在值类型上模板化的迭代器)可以使用称为类型擦除的技术来实现(您可以在那里找到 any_iterator 实现),就像 boost 的 function 一样和任何类型。

基本思想是:

 //less templated interface
 template <class T>
 class any_iterator_base
 {
     virtual void increment() = 0;
     /*...*/
 };

 //derived class templated on iterator type
 template <class Iter, class T>
 class any_iterator_impl: public any_iterator_base<T>
 {
     Iter it;
     virtual void increment() { ++it; }
     /*...*/
 };

 //and a class for the user which makes it all act like a regular value type
 template <class T>
 class any_iterator
 {
     shared_ptr<any_iterator_base<T> > it;
 public:
     template <class Iter>
     any_iterator(Iter iterator): it(new any_iterator_impl<Iter, T>(iterator)) {}
     any_iterator& operator++() { it->increment(); return *this; }
     //...
 };

 int main()
 {
      std::vector<int> vec;
      any_iterator<int> it = vec.begin();
      //...
 }

它可能比这更复杂(例如,需要做一些关于描述和强制迭代器类别的事情?,比较两个any_iterators如何工作(双重调度/RTTI?))。

STL (which is a subset of the standard library) does not use OOP (as in runtime polymorphism) at all and that's by design.

With your design, wouldn't there be problems returning iterators by value (covariance doesn't work for value types)? That is, wouldn't inevitably the whole thing either have to rely on static members (that you can return by reference) or on heap-allocated iterators? The latter would seem rather awkward in a non-garbage-collected language.

What you are describing (an iterator templated on value type) can be achieved using a technique called type-erasure (and you can find any_iterator implementations out there) just like boost's function and any types.

The basic idea is:

 //less templated interface
 template <class T>
 class any_iterator_base
 {
     virtual void increment() = 0;
     /*...*/
 };

 //derived class templated on iterator type
 template <class Iter, class T>
 class any_iterator_impl: public any_iterator_base<T>
 {
     Iter it;
     virtual void increment() { ++it; }
     /*...*/
 };

 //and a class for the user which makes it all act like a regular value type
 template <class T>
 class any_iterator
 {
     shared_ptr<any_iterator_base<T> > it;
 public:
     template <class Iter>
     any_iterator(Iter iterator): it(new any_iterator_impl<Iter, T>(iterator)) {}
     any_iterator& operator++() { it->increment(); return *this; }
     //...
 };

 int main()
 {
      std::vector<int> vec;
      any_iterator<int> it = vec.begin();
      //...
 }

It may be more complicated than that (e.g need to do something about describing and enforcing iterator category?, how would comparing two any_iterators work (double dispatch/RTTI?)).

失去的东西太少 2024-10-04 18:14:04

在 STL 中看不到很多“接口”风格的抽象基类的原因是它非常依赖 C++ 模板。当您使用 C++ 模板时,几乎任何类,无论其出身是什么,只要它支持模板尝试使用的所有方法,都可以使用。

有某种隐含的接口,但实际上没有必要将其写出来。在我自己的编码中,我倾向于编写一个无论如何,只是为了方便用户,但这不是 STL 编写者的工作方式。

The reason you don't see a lot of "interface"-style abstract base classes in the STL is because it relies so heavily on C++ templates. When you use C++ templates, pretty much any class whatsoever, no matter what its parentage may be, will do as long as it supports all the methods the template tries to use.

There is sort of an implied interface, but actually writing it out is unnessecary. In my own coding I tend to write one anyway, just as a convenience to the user, but that isn't how the STL writers roll.

云雾 2024-10-04 18:14:04

STL 结构在定义时并未考虑继承。为任何 stl 集合的子类化提供一个好的案例并不容易。考虑到这一点,stl 不会让您“支付”任何可能与普通类继承相关的成本。如果要使用虚拟方法,则优化器无法内联它们。

此外,STL 希望与不可能从顶级抽象基类继承的事物(例如数组)兼容。

STL structures are not defined with inheritance in mind. It's not easy to make a good case for subclassing any of the stl collections. with that in mind, the stl does not make you 'pay' any cost that might have been associated with normal class inheritance. If virtual methods were to be used, then they could not be inlined by the optimizer.

Also, STL wants to be compatible with things that could not possibly inherit from a top-level abstract base class, like arrays.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文