2017-09-25 115 views
2

目前我想知道如何正確使用std::unique_ptr作爲const正確性的成員變量。const使用std :: unique_ptr/std :: shared_ptr正確組合

下面的例子允許改變由my_foo所擁有的內容,儘管正在常量:

#include <iostream> 
#include <memory> 

struct foo { 
    foo() : value_ptr_(std::make_unique<int>(3)) {} 
    void increment() const { 
     ++(*value_ptr_); 
    } 
    int get_value() const { 
     return *value_ptr_; 
    } 
    std::unique_ptr<int> value_ptr_; 
}; 

int main() { 
    const foo my_foo; 
    std::cout << my_foo.get_value() << std::endl; 
    my_foo.increment(); // But my_foo is const! 
    std::cout << my_foo.get_value() << std::endl; 
} 

更換std::make_unique<T>std::make_unique<const T>好像乍一看很好的解決方案。然而,這不允許改變my_foo內容,即使它是非常量:

#include <iostream> 
#include <memory> 

struct foo { 
    foo() : value_ptr_(std::make_unique<int>(3)) {} 
    void increment() { 
     ++(*value_ptr_); 
    } 
    int get_value() const { 
     return *value_ptr_; 
    } 
    std::unique_ptr<const int> value_ptr_; 
}; 

int main() { 
    foo my_foo; 
    std::cout << my_foo.get_value() << std::endl; 
    my_foo.increment(); // compiler error 
    std::cout << my_foo.get_value() << std::endl; 
} 

擁有一個像這個小例子,當然不是非常有意義的指針爲int,但在實際的代碼unique_ptr把持不住一個指向多態的基類的指針,即一個我們無法簡單地按值存儲的對象。

那麼這種情況怎麼處理得更好呢?

+1

我不知道爲什麼你允許在'const foo'對象上調用'increment'嗎?基於這個名字,不應該被允許在這種情況下被調用 – UnholySheep

+0

我想象的類似於你用原始指針做什麼。某種包裝類。 – juanchopanza

+3

似乎是[std :: experimental :: propagate_const](http://en.cppreference.com/w/cpp/experimental/propagate_const)的用途。但是我沒有足夠的知識來寫答案。 –

回答

2

可以繼承std::unique_ptr並覆蓋只有3(4 unique_ptr<T[]>)方法,提供常量/非const重載:

template <typename T> 
struct propagating_unique_ptr : std::unique_ptr<T> { 
    using unique_ptr<T>::unique_ptr; 
    using unique_ptr<T>::operator =; 

    const T *get() const noexcept { 
     return unique_ptr<T>::get(); 
    } 
    T *get() noexcept { 
     return unique_ptr<T>::get(); 
    } 

    const T &operator *() const noexcept { 
     return unique_ptr<T>::operator *(); 
    } 
    T &operator *() noexcept { 
     return unique_ptr<T>::operator *(); 
    } 

    const T *operator ->() const noexcept { 
     return unique_ptr<T>::get(); 
    } 
    T *operator ->() noexcept { 
     return unique_ptr<T>::get(); 
    } 
}; 
+0

非常感謝。您的解決方案效果很好,我喜歡它,因爲該模式已被抽象出來,實際的類「foo」不能被修改很多。 :) [test1](https://ideone.com/pY0NlE)[test2](https://ideone.com/YRN1Lb)[test3](https://ideone.com/FKYBhT) –

1

我這樣做的方式是通過提供一個內部協議來提供對底層實現的正確引用的引用的訪問。

事情是這樣的:

struct foo { 
    // standard (in your codebase) protocol to express the impl base class 
    using underlying_impl = int; 

    // standard protocol to express ownership semantics 
    using implementation_handle = std::unique_ptr<underlying_impl>; 

    // construction via private 'construct' protocol  
    foo() : value_ptr_(construct(3)) {} 

    // all internal access to the implementation via a the protocol 
    // of get_impl() 
    auto operator++() -> foo& 
    { 
     // not-const - compiles fine 
     ++get_impl(); 
     return *this; 
    } 

    void increment() const { 
// now won't compile - get_impl() propagates const correctly 
//  ++get_impl(); 
    } 

private: 

    static auto construct(int val) -> implementation_handle 
    { 
     return std::make_unique<underlying_impl>(val); 
    } 

    // two versions of get_impl() - const and mutable 
    auto get_impl() const -> underlying_impl const& 
    { 
     return *value_ptr_; 
    } 

    auto get_impl() -> underlying_impl& 
    { 
     return *value_ptr_; 
    } 

    // actual storage of the implementation handle 
    implementation_handle value_ptr_; 
}; 
相關問題