簡體   English   中英

創建庫以覆蓋迭代器的 operator*() - 風險懸空指針

[英]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-pointerby-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數據或指針(可以是適配器真實數據結構)?

或者,如果從頭開始編碼(比修改我的代碼)更容易回答,那就去做吧。 :)

我的解決方法

當前代碼通過指針緩存。
解決方法的主要思想是按值緩存。

解決方法 1(始終“按值”)

適配器緩存COLLECTION
這是主要的變化:-

COLLECTION collection_;    //<------ #1 
//changed from   .... COLLECTION* collection_;

壞處:-

  • 整個數據結構(例如std::vector )將被值復制 - 浪費資源。
    (直接用於std::vector

解決方法 2(兩個版本的庫,最好?)

我將創建 2 個版本的庫 - AdapterValueAdapterPointer
我還必須創建相關類( WidgetAdapterIterator等)。

  • AdapterValue -按值 (專為utilityAdapter()設計)
  • AdapterPointer - by 指針 (專為std::vector設計)

壞處:-

  • 大量重復代碼 = 低可維護性
  • 用戶(編碼人員)必須非常清楚選擇哪一個 = 低穩健性

解決方法 3(檢測類型)

我可以使用模板專業化來做到這一點:-

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM