![](/img/trans.png)
[英]how to tell compiler to do "range based for loop" in string vector?
[英]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.