繁体   English   中英

如何在多态类(包含向量和集合)中支持基于范围的循环?

[英]How to support a range based loop in polymorphic classes (containing vector and set)?

我想迭代我的 class 中的一些项目

for (const auto i: myclass) { /* do stuff with i */}

为此,我想公开任何 STL 容器恰好将我的数据存储在myclass中的迭代器。

我的 class 是多态的,具有以下层次结构:

#include <set>
#include <vector>

class Base_t {
public:
    //works for the set, but not for the vector
    //using iterator_t = decltype(std::declval<std::set<int>>().cbegin());

    //does not work, see error below.
    using iterator_t = std::iterator<std::input_iterator_tag, int>;

    virtual iterator_t begin() = 0;
    virtual iterator_t end() = 0;
};

class MyVector_t: public Base_t {
    std::vector<int> v;
public:
    iterator_t begin() override { return v.begin(); }
    iterator_t end() override {return v.end(); }
};

class MyTree_t: public Base_t {
    std::set<int> s;
public:
    iterator_t begin() override { return s.begin(); }
    iterator_t end() override {return s.end(); }
};

//#################################################
//the class using these things looks like:
//#################################################
class Worker_t {
    Base_t& container;  //polymorphic container
public:
    Worker_t(const bool UseTree): container(*CreateContainer(UseTree)) {}

    Base_t* CreateContainer(const bool UseTree) const {
        if (UseTree) { return new MyTree_t(); }
        else         { return new MyVector_t(); }
    }

    //need an iterator into the container to use it.
    void DoStuff() { for (const auto i: container) { printf("%i",i); } } 
};

int main(const int argc, const char* argv[]) {
    const auto UseTree = true;
    Worker_t worker1(UseTree);
    worker1.DoStuff();

    Worker_t worker2(!UseTree);
    worker2.DoStuff();
}

这给出了错误:

从“std::set::const_iterator”(又名“__tree_const_iterator<int, std::__tree_node<int, void *> *, long>”)类型的返回值到 function 返回类型“Base_t::it”没有可行的转换(又名 'iterator<std::input_iterator_tag, int>')

我可以使层次结构成为 CRTP 设置,但我需要这些方法是虚拟的。

template <typename T>
class Base_t<T> {
    ....
}; 

class Derived: Base_t<Derived> {
    ...
};

对我不起作用,因为使用类的代码只知道 Base_t,而不知道其他任何东西,即使用这些东西的 class 看起来像:

class Worker_t {
    Base_t& container;  //polymorphic container
public:
    Worker_t(const bool UseTree): container(*CreateContainer(UseTree)) {}
   
    Base_t* CreateContainer(const bool UseTree) const {
        if (UseTree) { return new MyTree_t(); }
        else         { return new MyVector_t(); }
    }

    //need an iterator into the container to use it.
    void DoStuff() { for (const auto i&: container) { /* do stuff*/ } } 
};

使代码工作所需的最小更改是什么,最好使用虚拟方法?

作为记录,我使用的是 c++20

苹果 clang 版本 13.1.6 (clang-1316.0.19.2)
目标:x86_64-apple-darwin21.3.0
线程 model:posix
安装目录:/Library/Developer/CommandLineTools/usr/bin

您正试图将运行时多态性强制放入为编译时多态性设计的盒子中。 这自然会产生问题。

迭代器机制基于能够询问迭代器问题的编译时机制。 迭代器tag与基类完全不同。 迭代器tag类型在运行时不做任何事情; 它们仅用于允许编译时元编程(基于模板或constexpr )检测迭代器的类型是否提供特定迭代器类别的接口。

在运行时多态中,基础 class 定义了一切。 一切,直接而明确。 派生类可以更改实现,但不能更改编译时接口。

迭代器的 value_type(迭代器“指向”的类型)是一个编译时构造。 vector<int>具有与vector<float>不同的迭代器类型。 它们都是 model 同一种迭代器(随机访问),但类型本身没有运行时关系。 迭代器类型在很大程度上不知道彼此,即使它们具有相同的迭代器类别和value_type

std::array<int, 5>std::vector<int>在相同的value_type上都有 RandomAccessIterators,但是这两种迭代器类型没有任何共同之处。 它们可以在相同的编译时多态接口中使用,但它们不能在运行时互换使用。

迭代器类别不是基类

由于基础 class 定义了一个返回具体类型的virtual接口,因此此类迭代器的所有属性都由基础 class定义。 派生类可以提供实际的值范围,但迭代器的所有编译时方面都必须在编译时固定。 这包括迭代器的基础类型。

如果您可以将所有派生类限制为使用具有特定迭代器的特定容器,那很好。 但是如果你想让他们用不同的迭代器类型定义他们自己的容器,那就有问题了。

解决这个问题的唯一方法是为您的基本 class 创建一个类型擦除的迭代器类型,它可以由任何迭代器填充,并且它本身使用运行时多态性来访问真正的迭代器类型。 这实现起来并不简单,并且涉及大量开销(因为迭代器上的每个操作现在都是某种形式的动态调度)。

此外,这样的迭代器只能 model 特定类型的迭代器。 因此,如果您希望派生类能够将数据存储在 RandomAccessIterator 或 ForwardIterator 中,您的类型擦除迭代器必须仅将其自身公开为 ForwardIterator:最低公分母。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM