C++ 如何处理const对象中非const引用成员的初始化?
假设你有一门课C++ 如何处理const对象中非const引用成员的初始化?,c++,constructor,const-correctness,C++,Constructor,Const Correctness,假设你有一门课 class C { int * i; public: C(int * v):i(v) {}; void method() const; //this method does not change i void method(); //this method changes i } 现在您可能需要定义这个类的const实例 const int *
class C
{
int * i;
public:
C(int * v):i(v) {};
void method() const; //this method does not change i
void method(); //this method changes i
}
现在您可能需要定义这个类的const实例
const int * k = whatever;
const C c1(k); //this will fail
但这将失败,因为非常量int C的构造函数C(int*v)
所以定义一个const int构造函数
C(const int * v):i(v) {}; //this will fail also
但这也会失败,因为C的成员“int*i”是非常量
在这种情况下该怎么办?使用可变的?铸造准备类的常量版本
编辑:在与Pavel(以下)讨论后,我对这个问题进行了一些调查。对我来说,C++所做的是不正确的。指针目标应为严格类型,这意味着您不能执行以下操作:
int i;
const int * ptr;
ptr = & i;
在这种情况下,语言语法将const
视为不更改指针目标的承诺。此外,int*const ptr
承诺不会更改指针值本身。因此,有两个地方可以应用const。然后,您可能希望您的类为指针建模(为什么不建模)。在这里,一切都在分崩离析。C++语法提供了一些方法,这些方法可以保证不改变字段的值本身,但是没有语法指出你的方法不会改变你的类指针的目标。
解决方法是定义两个类,例如const_C
和C
。然而,这不是一条皇家大道。有了模板,他们的局部专业化很难不陷入混乱。此外,所有可能的参数变化,如常量C&arg
,常量C&arg
,常量C&arg
,常量C&arg
,看起来都不漂亮。我真的不知道该怎么办。使用单独的类或const_类型转换,每种方法似乎都是错误的
在这两种情况下,我应该将不修改指针目标的方法标记为const吗?或者只需遵循传统的路径,即const方法不改变对象本身的状态(const方法不关心指针目标)。在我的例子中,所有的方法都是常量,因为类对指针进行建模,所以指针本身就是
T*const
。但很明显,其中一些修改了指针的目标,而另一些则没有修改。您的示例并没有失败,k
是按值传递的。成员i
是“隐式常量”,因为当实例为常量时,不能更改C
的直接成员。康斯特内斯说,初始化后不能更改成员,但当然允许使用初始化列表中的值对其进行初始化-否则如何给它们一个值 不起作用的是调用构造函数而不将其公开;) 更新地址更新问题:
是的,C++有时会迫使你去做一些冗长的事情,但正确性是一种常见的标准行为,你不能重新定义而不破坏期望。已经解释了一个常见的习惯用法,它在经过验证的库(如STL)中用于解决这种情况
有时,你不得不接受语言有局限性,并且仍然要满足界面用户的期望,即使这意味着要应用一个明显次优的解决方案。你的问题没有意义。你从哪里得到这些“这将失败”的预测?这些都不是真的 首先,构造函数的参数是否声明为
const
是完全无关的。当您通过值传递时(在您的案例中),您可以在任何情况下将const
对象作为参数传递,无论该参数是否声明为const
其次,从构造函数的角度来看,对象不是常量。无论您正在构造什么类型的对象(常量或非常量),从构造函数中,对象永远不会是常量。所以不需要可变的或任何东西
你为什么不试着编译你的代码(看看什么都不会失败),而不是做出一些奇怪的、毫无根据的预测,某些东西“会失败”听起来你想要一个对象,它可以包装int*
(然后表现为非常量),或者int const*
(然后表现为常量)。你不可能用一个类就把它做好
事实上,应用于类的const
应该像这样更改其语义的概念是错误的-如果类对指针或迭代器建模(如果它包装了指针,很可能就是这样),那么应用于它的const
应该只意味着它本身不能更改,不应暗示与所指价值相关的任何内容。您应该考虑STL为其容器所做的事情——这正是为什么它具有明显的<代码>迭代器< /代码>和<代码> CistaTyrAtter < /Case>类,两者都是不同的,但前者隐含地转换为后者。同样,在STL中,常量迭代器
与常量迭代器
不同!所以也要这样做
[编辑]这里有一个棘手的方法,可以最大限度地在C
和const\U C
之间重用代码,同时确保整个过程中的const正确性,而不是深入U.B.(使用const\U cast
):
模板
结构指针指向常量;
模板
结构指针指向常量{typedef const T*type;};
模板
结构指针指向常量{typedef T*type;};
模板
结构C_字段{
typename指针指向常量::type i;
//对所有字段重复此操作
};
模板
类const_C_base{
公众:
int method()常量{//非变异方法示例
return*self().i;
}
私人:
const-Derived&self()const{return*static_cast(this);}
};
模板
C_类基地:公共建筑C_基地{
公众:
int method(){//mutating方法示例
return++*self().i;
}
私人:
派生的&self(){return*static_cast(this);}
};
类常量C:公共常量C_基,私有C_字段{
友元类const_C_base;
};
丙级:聚氨酯
template<class T, bool IsConst>
struct pointer_to_maybe_const;
template<class T>
struct pointer_to_maybe_const<T, true> { typedef const T* type; };
template<class T>
struct pointer_to_maybe_const<T, false> { typedef T* type; };
template<bool IsConst>
struct C_fields {
typename pointer_to_maybe_const<int, IsConst>::type i;
// repeat for all fields
};
template<class Derived>
class const_C_base {
public:
int method() const { // non-mutating method example
return *self().i;
}
private:
const Derived& self() const { return *static_cast<const Derived*>(this); }
};
template<class Derived>
class C_base : public const_C_base<Derived> {
public:
int method() { // mutating method example
return ++*self().i;
}
private:
Derived& self() { return *static_cast<Derived*>(this); }
};
class const_C : public const_C_base<const_C>, private C_fields<true> {
friend class const_C_base<const_C>;
};
class C : public C_base<C>, private C_fields<false> {
friend class C_base<C>;
};
const C c1(...)
int* const i;
const int* whatever = ...;
int* const i = whatever; // error
template <typename T>
union MutatedPtr
{
protected:
const T * const_ptr;
T * ptr;
public:
/**
* Conversion constructor.
* @param ptr pointer.
*/
MutatedPtr(const T * ptr): const_ptr(ptr) {};
/**
* Conversion to T *.
*/
operator T *() {return ptr;}
/**
* Conversion to const T *.
*/
operator const T *() const {return const_ptr;}
};
template< typename DataPtrT >
struct BaseImage
{
BaseImage( const DataPtrT & data ) : m_data( data ) {}
DataPtrT getData() { return m_data; } // notice that if DataPtrT is const
// internally, this will return
// the same const type
DataPtrT m_data;
};
template< typename DataPtrT >
struct DerivedImage : public BaseImage<DataPtrT>
{
};
template< typename OutTypeT, typename inTypeT )
image_cast< shared_ptr<OutTypeT> >( const shared_ptr<InTypeT> & inImage )
{
return shared_ptr<OutTypeT>( new OutTypeT( inImage->getData() ) );
}