将常量传播到成员变量所指向的数据 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_ };
然而,如果constness会传播到指向的对象,它有时会非常方便(我自愿使用PImpl习惯用法,因为这是我认为“constness传播”非常有用的情况之一) 当使用指针时,这可以通过使用某种智能指针和常量上重载的运算符轻松实现:将常量传播到成员变量所指向的数据 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
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
,以获得所需的行为
所以我有几个问题:
Pimpl
(如下)通过执行深度复制很好地规避了问题,unique\u ptr
通过不可复制来规避问题。当然,如果指针对象由一个实体拥有,那么就容易多了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可以访问指针)