将常量传播到成员变量所指向的数据 p>对于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_ };

将常量传播到成员变量所指向的数据 p>对于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_ };,c++,pointers,constants,smart-pointers,C++,Pointers,Constants,Smart Pointers,然而,如果constness会传播到指向的对象,它有时会非常方便(我自愿使用PImpl习惯用法,因为这是我认为“constness传播”非常有用的情况之一) 当使用指针时,这可以通过使用某种智能指针和常量上重载的运算符轻松实现: template < typename T > class const_propagating_ptr { public: const_propagating_ptr( T * ptr ) : ptr_( ptr ) {} T

然而,如果constness会传播到指向的对象,它有时会非常方便(我自愿使用PImpl习惯用法,因为这是我认为“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_;
};
模板
类常数传播
{
公众:
常数传播的ptr(T*ptr):ptr(ptr){
T&运算符*(){return*ptr}
T常量和运算符*()常量{return*ptr_;}
T*运算符->(){return ptr_;}
T常量*运算符->()常量{return ptr_;}
//赋值运算符(?),get()方法(?),reset()方法(?)
// ...
私人:
T*ptr;
};
现在,我只需要将
SomeClass::impl\u
修改为
const\u propagating\u ptr
,以获得所需的行为

所以我有几个问题:

  • 康斯特内斯传播是否存在一些我忽略的问题
  • 如果没有,是否有任何库提供类来获取常量传播
  • 通用智能指针(unique_ptr、shared_ptr等)提供一些方法来获得这种行为(例如通过模板参数),这不是很有用吗
  • 正如@Alf p.Steinbach所指出的,复制指针会产生一个指向同一底层对象的非常量对象
    Pimpl
    (如下)通过执行深度复制很好地规避了问题,
    unique\u ptr
    通过不可复制来规避问题。当然,如果指针对象由一个实体拥有,那么就容易多了

  • 传播常量,但它并不完全是指针(尽管它对OptionalPointee概念进行了建模)。我不知道还有这样的图书馆

  • 我希望他们默认提供。添加另一个模板参数(我想是traits类)似乎不值得这么麻烦。然而,这将从根本上改变经典指针的语法,因此我不确定人们是否愿意接受它


  • 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&> {};
    
    模板
    类Pimpl
    {
    公众:
    /**
    *类型
    */
    T值;
    typedef const T const_值;
    typedef T*指针;
    typedef const T*const_指针;
    typedef T&reference;
    类型定义常数T和常数U参考;
    /**
    *四人帮
    */
    Pimpl():_值(新的T()){}
    显式Pimpl(常数参考v):_值(新的T(v)){}
    Pimpl(常量Pimpl和rhs):_值(新T(*(rhs._值))){
    Pimpl和运算符=(常量Pimpl和rhs)
    {
    Pimpl tmp(rhs);
    掉期(tmp);
    归还*这个;
    }//操作员=
    ~Pimpl(){boost::checked_delete(_value);}
    无效掉期(Pimpl和rhs)
    {
    指针温度(rhs.\U值);
    rhs._值=_值;
    _数值=温度;
    }//交换
    /**
    *数据存取
    */
    指针get(){return\u value;}
    常量指针get()常量{返回值;}
    引用运算符*(){return*_value;}
    const_引用运算符*()const{return*_value;}
    指针运算符->(){返回_值;}
    常量指针运算符->()常量{返回值;}
    私人:
    指针_值;
    }; // 类Pimpl
    //交换
    模板
    无效掉期(Pimpl&lhs,Pimpl&rhs){lhs.swap(rhs);}
    //不与指针或引用一起使用
    模板类Pimpl{};
    模板类Pimpl{};
    
    一种方法是不直接使用指针,除非通过两个访问器函数

    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();
        }
    };
    

    对于记录,我刚刚发现确实提供了一个常量传播指针()。它看起来与问题中的一样,只是它还删除了析构函数中的包装指针,并用于实现类似的(但不可复制)。

    如果您认为它应该“传播”常量,那么这意味着您并不真的相信它是指针(或引用),但是您认为它是一个容器:如果对象是常量时值是常量,那是因为对象包含值

    因此,复制对象会复制值,至少在逻辑上是这样(CoW)

    如果您坚持认为它是一个指针/引用,您可以在共享包含的值时复制对象,那么就有一个不健全(矛盾)的接口

    结论:下定决心。它是容器或指针


    根据定义,指针不会传播常数。

    如果我只是复制智能指针会怎么样?瞧,我有一个非常量。
    T常量*常量运算符->()常量{return ptr}
    -可能不需要第二个
    const
    here@Alf而@robin:我给出的可能实现的草图可能到处都是bug(尽管它的大小很小:),这不是问题的中心。不过,非常感谢您的反馈!关于复制问题,我现在不知道我们如何才能阻止它成为可能,但通常你不能完全阻止自己射中自己的脚(例如,你可以总是
    const\u扔掉constness,这并不意味着const是无用的)。关于第二条评论,你是对的@robin,我错误地这样做是为了防止调用方…[cont.]修改
    ptr\uu
    ,这是愚蠢的,因为指针是通过值返回的…我将立即删除该常量。现在有一个关于C++17的官方建议这是我最初尝试的解决方案,除了我使用了一个嵌套类实例来封装指针,以防止外部类访问指针(只有getter可以访问指针)