将常量传播到成员变量指向的数据
对于 C++ 新手来说,const 成员函数被允许在类引用的对象上调用非 const 方法(通过指针或引用)通常很困惑.例如,以下是完全正确的:
It is often quite confusing to C++ newcomers that const member functions are allowed to call non-const methods on objects referenced by the class (either by pointer or reference). For example, the following is perfectly correct:
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 传播"非常有用的情况之一).
However, it would sometimes be rather handy if the constness would propagate to pointed objects (I voluntarily used the PImpl idiom because it is one of the case where I think "constness propagation" would be very useful).
当使用指针时,这可以很容易地通过使用某种智能指针来实现,该指针带有在 const 上重载的运算符:
When using pointers, this can easily be achieved by using some kind of smart pointer with operators overloaded on 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
即可获得想要的行为.
Now, I just need to modify SomeClass::impl_
to be a const_propagating_ptr<SomeClassImpl>
to obtain the wanted behavior.
所以我对此有几个问题:
So I have a few questions about this:
- 是否存在一些我忽略的常量传播问题?
- 如果没有,是否有任何库提供类来获得 constness 传播?
- 常见的智能指针(unique_ptr、shared_ptr 等)提供某种手段来获得这种行为(例如通过模板参数)不是很有用吗?
推荐答案
正如@Alf P. Steinbach 指出的那样,您监督了这样一个事实,即复制您的指针会产生一个指向同一底层对象的非常量对象.
Pimpl
(下)通过执行深拷贝很好地规避了这个问题,unique_ptr
通过不可复制来规避它.当然,如果指针对象由单个实体拥有,则要容易得多.
As @Alf P. Steinbach noted, you oversaw the fact that copying your pointer would yield a non-const object pointing to the same underlying object.
Pimpl
(below) nicely circumvent the issue by performing a deep-copy,unique_ptr
circumvents it by being non-copyable. It is much easier, of course, if the pointee is owned by a single entity.
Boost.Optional 传播 const-ness,但它并不完全是一个指针(尽管它模拟了 OptionalPointee 概念).我知道没有这样的其他图书馆.
Boost.Optional propagates const-ness, however it's not exactly a pointer (though it models the OptionalPointee concept). I know of no such other library.
我希望他们默认提供它.添加另一个模板参数(我猜是特征类)似乎不值得麻烦.但是,这会从根本上改变经典指针的语法,所以我不确定人们是否准备好接受它.
I would favor that they provide it by default. Adding another template parameter (traits class I guess) does not seem worth the trouble. However that would radically change the syntax from a classic pointer, so I am not sure that people would be ready to embrace it.
<小时>
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&> {};
相关文章