簡體   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