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