[英]Create library to override operator*() of iterator - risk dangling pointer
我正在嘗試創建自己的boost::adaptors::transformed
。
這是相關的提升代碼。
這是它的用法(從LogicStuff 的 SO 答案中修改):-
C funcPointer(B& b){
//"funcPointer" is function convert from "B" to "C"
return instance-of-C
}
MyArray<B> test; //<-- any type, must already have begin() & end()
for(C c : test | boost::adaptor::transformed(funcPointer)) {
//... something ....
}
結果將與以下相同:-
for(auto b : test) {
C c = funcPointer(b);
//... something ...
}
我創建的CollectAdapter
旨在像boost::adaptor::transformed
一樣工作。
它在大多數常見情況下都可以正常工作。
有問題的部分是CollectAdapter
- 我圖書館的核心。
我不知道我是否應該緩存collection_
by-pointer或by-value 。
CollectAdapter封裝底層collection_
(例如指向std::vector<>
指針):-
template<class COLLECTION,class ADAPTER>class CollectAdapter{
using CollectAdapterT=CollectAdapter<COLLECTION,ADAPTER>;
COLLECTION* collection_; //<---- #1 problem? should cache by value?
ADAPTER adapter_; //<---- = func1 (or func2)
public: CollectAdapter(COLLECTION& collection,ADAPTER adapter){
collection_=&collection;
adapter_=adapter;
}
public: auto begin(){
return IteratorAdapter<
decltype(std::declval<COLLECTION>().begin()),
decltype(adapter_)>
(collection_->begin(),adapter_);
}
public: auto end(){ ..... }
};
IteratorAdapter
(上面使用)封裝了底層迭代器,改變operator*
行為:-
template<class ITERATORT,class ADAPTER>class IteratorAdapter : public ITERATORT {
ADAPTER adapter_;
public: IteratorAdapter(ITERATORT underlying,ADAPTER adapter) :
ITERATORT(underlying),
adapter_(adapter)
{ }
public: auto operator*(){
return adapter_(ITERATORT::operator*());
}
};
CollectAdapterWidget
(下面使用)只是一個構建CollectAdapter
的輔助類。
它可以像這樣使用:-
int func1(int i){ return i+10; }
int main(){
std::vector<int> test; test.push_back(5);
for(auto b:CollectAdapterWidget::createAdapter(test,func1)){
//^ create "CollectAdapter<std::vector<int>,func1>" instance
//here, b=5+10=15
}
}
上面的代碼在大多數情況下都可以正常工作,除非COLLECTION
是臨時對象。
更具體地說,當我創建適配器適配器的適配器時,可能會出現懸空指針... 。
int func1(int i){ return i+10; }
int func2(int i){ return i+100; }
template<class T> auto utilityAdapter(const T& t){
auto adapter1=CollectAdapterWidget::createAdapter(t,func1);
auto adapter12=CollectAdapterWidget::createAdapter(adapter1,func2);
//"adapter12.collection_" point to "adapter1"
return adapter12;
//end of scope, "adapter1" is deleted
//"adapter12.collection_" will be dangling pointer
}
int main(){
std::vector<int> test;
test.push_back(5);
for(auto b:utilityAdapter(test)){
std::cout<< b<<std::endl; //should 5+10+100 = 115
}
}
這將導致運行時錯誤。 這是懸空指針演示。
實際使用中,如果界面比較給力,比如使用|
操作員,錯誤將更難被發現:-
//inside "utilityAdapter(t)"
return t|func1; //OK!
return t|func1|func2; //dangling pointer
如何改進我的庫以修復此錯誤,同時保持性能、健壯性和可維護性接近同一水平?
換句話說,如何優雅地緩存COLLECTION
數據或指針(可以是適配器或真實數據結構)?
或者,如果從頭開始編碼(比修改我的代碼)更容易回答,那就去做吧。 :)
當前代碼通過指針緩存。
解決方法的主要思想是按值緩存。
讓適配器緩存COLLECTION
的值。
這是主要的變化:-
COLLECTION collection_; //<------ #1
//changed from .... COLLECTION* collection_;
壞處:-
std::vector
)將被值復制 - 浪費資源。std::vector
) 我將創建 2 個版本的庫 - AdapterValue
和AdapterPointer
。
我還必須創建相關類( Widget
、 AdapterIterator
等)。
AdapterValue
-按值。 (專為utilityAdapter()
設計)AdapterPointer
- by 指針。 (專為std::vector
設計)壞處:-
我可以使用模板專業化來做到這一點:-
If( COLLECTION is an "CollectAdapter" ){ by value }
Else{ by pointer }
壞處:-
很抱歉很長的帖子。
我個人會使用模板特化——然而,不是特化原始模板,而是一個嵌套類:
template<typename Collection, typename Adapter>
class CollectAdapter
{
template<typename C>
class ObjectKeeper // find some better name yourself...
{
C* object;
public:
C* operator*() { return object; };
C* operator->() { return object; };
};
template<typename C, typename A>
class ObjectKeeper <CollectAdapter<C, A>>
{
CollectAdapter<C, A> object;
public:
CollectAdapter<C, A>* operator*() { return &object; };
CollectAdapter<C, A>* operator->() { return &object; };
};
ObjectKeeper<Collection> keeper;
// now use *keeper or keeper-> wherever needed
};
然后,外部類通過始終使用指針來覆蓋這兩種情況,而嵌套類則隱藏差異。
當然,不完整(您還需要向外部和內部類添加適當的構造函數,例如),但它應該給您一個想法......
您甚至可以允許用戶選擇她/他是否要復制:
template<typename Collection, typename Adapter, bool IsAlwaysCopy = false>
class CollectAdapter
{
template<typename C, bool IsCopy>
class ObjectWrapper // find some better name yourself...
{
C* object;
public:
C* operator*() { return object; };
C* operator->() { return object; };
};
template<typename C>
class ObjectWrapper<C, true>
{
C object;
public:
C* operator*() { return &object; };
C* operator->() { return &object; };
};
// avoiding code duplication...
template<typename C, bool IsCopy>
class ObjectKeeper : public ObjectWrapper<C, IsCopy>
{ };
template<typename C, typename A, bool IsCopy>
class ObjectKeeper <CollectAdapter<C, A>, IsCopy>
: public ObjectWrapper<CollectAdapter<C, A>, true>
{ };
ObjectKeeper<Collection> keeper;
};
在我的indexed_view
中,如果它是一個右值,我存儲集合的值,如果它是一個左值,我存儲一個引用。 你可以在這里做同樣的事情:重載你的operator|
對於右值和左值。
template<typename Collection,typename Filter>
auto operator|(Collection&& collection,Filter filter){
return create_adapter_for_rvalue_collection(collection,filter);
}
template<typename Collection,typename Filter>
auto operator|(Collection const& collection,Filter filter){
return create_adapter_for_const_lvalue_collection(collection,filter);
}
template<typename Collection,typename Filter>
auto operator|(Collection & collection,Filter filter){
return create_adapter_for_non_const_lvalue_collection(collection,filter);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.