[英]Propagate constness to data pointed by member variables
對於C ++新手來說,允許const成員函數在類引用的對象(通過指針或引用)上調用非const方法通常會讓人感到困惑。 例如,以下內容完全正確:
class SomeClass
{
class SomeClassImpl;
SomeClassImpl * impl_; // PImpl idiom
public:
void const_method() const;
};
struct SomeClass::SomeClassImpl
{
void non_const_method() { /*modify data*/ }
};
void SomeClass::const_method() const
{
impl_->non_const_method(); //ok because impl_ is const, not *impl_
};
但是,如果constness傳播到尖頭對象,它有時會非常方便(我自願使用PImpl習語,因為它是我認為“constness傳播”非常有用的情況之一)。
使用指針時,可以通過使用某種智能指針輕松實現這一點,操作符在constness上重載:
template < typename T >
class const_propagating_ptr
{
public:
const_propagating_ptr( T * ptr ) : ptr_( ptr ) {}
T & operator*() { return *ptr_; }
T const & operator*() const { return *ptr_; }
T * operator->() { return ptr_; }
T const * operator->() const { return ptr_; }
// assignment operator (?), get() method (?), reset() method (?)
// ...
private:
T * ptr_;
};
現在,我只需將SomeClass::impl_
修改為const_propagating_ptr<SomeClassImpl>
即可獲得所需行為。
所以我對此有幾個問題:
正如@Alf P. Steinbach所說,你監督了一個事實,即復制你的指針會產生一個指向同一個底層對象的非const對象。 Pimpl
(下面)通過執行深層復制很好地避開了這個問題, unique_ptr
通過不可復制來繞過它。 當然,如果指針對象由單個實體擁有,則更容易。
Boost.Optional傳播const-ness,但它並不是一個指針(盡管它模擬了OptionalPointee概念)。 我知道沒有其他這樣的圖書館。
我贊成他們默認提供它。 添加另一個模板參數(我猜的特征類)似乎不值得麻煩。 然而,這將徹底改變經典指針的語法,所以我不確定人們是否願意接受它。
Pimpl
類的代碼
template <class T>
class Pimpl
{
public:
/**
* Types
*/
typedef T value;
typedef const T const_value;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
/**
* Gang of Four
*/
Pimpl() : _value(new T()) {}
explicit Pimpl(const_reference v) : _value(new T(v)) {}
Pimpl(const Pimpl& rhs) : _value(new T(*(rhs._value))) {}
Pimpl& operator=(const Pimpl& rhs)
{
Pimpl tmp(rhs);
swap(tmp);
return *this;
} // operator=
~Pimpl() { boost::checked_delete(_value); }
void swap(Pimpl& rhs)
{
pointer temp(rhs._value);
rhs._value = _value;
_value = temp;
} // swap
/**
* Data access
*/
pointer get() { return _value; }
const_pointer get() const { return _value; }
reference operator*() { return *_value; }
const_reference operator*() const { return *_value; }
pointer operator->() { return _value; }
const_pointer operator->() const { return _value; }
private:
pointer _value;
}; // class Pimpl<T>
// Swap
template <class T>
void swap(Pimpl<T>& lhs, Pimpl<T>& rhs) { lhs.swap(rhs); }
// Not to be used with pointers or references
template <class T> class Pimpl<T*> {};
template <class T> class Pimpl<T&> {};
一種方法是不通過兩個訪問器函數直接使用指針。
class SomeClass
{
private:
class SomeClassImpl;
SomeClassImpl * impl_; // PImpl idiom - don't use me directly!
SomeClassImpl * mutable_impl() { return impl_; }
const SomeClassImpl * impl() const { return impl_; }
public:
void const_method() const
{
//Can't use mutable_impl here.
impl()->const_method();
}
void non_const_method() const
{
//Here I can use mutable_impl
mutable_impl()->non_const_method();
}
};
為了記錄,我發現Loki庫確實提供了一個const傳播指針( ConstPropPtr<T>
)。 它看起來就像一個在的問題,但它還會刪除其析構函數的指針包裹,並用它來實現Pimpl
類相似, 由@Matthieu提出的一個 (但不可拷貝)。
如果你認為它應該“傳播”const-ness,那么它意味着你並不真的相信它是一個指針 (或引用),但你相信它是一個容器 :如果當對象是常量時值是常量,那么它是因為對象包含值。
因此,復制對象至少在邏輯上(CoW)復制值。
如果你堅持認為它是一個指針/引用,你可以在共享包含的值時復制對象,那么你就有一個不健全(矛盾)的界面 。
結論 :下定決心。 它是容器或指針。
根據定義 ,指針不會傳播常量。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.