簡體   English   中英

將constness傳播到成員變量指向的數據

[英]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>即可獲得所需行為。

所以我對此有幾個問題:

  1. 我忽略了常量傳播的一些問題嗎?
  2. 如果沒有,是否有任何庫提供類來獲得常量傳播?
  3. 常見的智能指針(unique_ptr,shared_ptr等)提供某種方法來獲取此行為(例如通過模板參數)不是很有用嗎?
  1. 正如@Alf P. Steinbach所說,你監督了一個事實,即復制你的指針會產生一個指向同一個底層對象的非const對象。 Pimpl (下面)通過執行深層復制很好地避開了這個問題, unique_ptr通過不可復制來繞過它。 當然,如果指針對象由單個實體擁有,則更容易。

  2. Boost.Optional傳播const-ness,但它並不是一個指針(盡管它模擬了OptionalPointee概念)。 我知道沒有其他這樣的圖書館。

  3. 我贊成他們默認提供它。 添加另一個模板參數(我猜的特征類)似乎不值得麻煩。 然而,這將徹底改變經典指針的語法,所以我不確定人們是否願意接受它。


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.

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