将constness传播到成员变量指向的数据

Luc*_*lle 24 c++ pointers const smart-pointers

对于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_
};
Run Code Online (Sandbox Code Playgroud)

但是,如果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_;
};
Run Code Online (Sandbox Code Playgroud)

现在,我只需要修改SomeClass::impl_是一个const_propagating_ptr<SomeClassImpl>获得想要的行为.

所以我对此有几个问题:

  1. 我忽略了常量传播的一些问题吗?
  2. 如果没有,是否有任何库提供类来获得常量传播?
  3. 常见的智能指针(unique_ptr,shared_ptr等)提供某种方法来获取此行为(例如通过模板参数)不是很有用吗?

Mat*_* M. 2

  1. 正如 @Alf P. Steinbach 所指出的,您看到了这样一个事实:复制指针会产生一个指向同一底层对象的非常量对象。Pimpl(如下)通过执行深度复制很好地规避了这个问题,unique_ptr通过不可复制来规避它。当然,如果受指点者由单一实体拥有,那就容易得多。

  2. Boost.Optional传播常量性,但它并不完全是一个指针(尽管它模拟了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&> {};
Run Code Online (Sandbox Code Playgroud)