c++ - 如何处理const对象中非const引用成员的初始化?

标签 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 int * k = whatever;
    const C c1(k); //this will fail

但这会失败,因为非 const int C 的构造函数 C(int * v)

所以你定义了一个const int构造函数

    C(const int * v):i(v) {}; //this will fail also

但这也会失败,因为 C 的成员“int * i”是非常量。

遇到这种情况怎么办?使用可变的?铸件?准备类的 const 版本?

编辑:在与 Pavel(下)讨论后,我对这个问题进行了一些调查。对我来说,C++ 所做的是不正确的。指针目标应该是严格类型,这意味着您不能执行以下操作:

int i;
const int * ptr;
ptr = & i;

在这种情况下,语言语法将 const 视为不更改指针目标的 promise 。此外,int * const ptr promise 不会更改指针值本身。因此,您有两个地方可以应用 const。那么您可能希望您的类(class)模拟一个指针(为什么不呢)。在这里,事情正在分崩离析。 C++ 语法提供了 const 方法,这些方法能够保证不会更改字段的值本身,但是没有语法指出您的方法不会更改类内指针的目标。

解决方法是定义两个类,例如 const_CC。然而,这不是一条皇家之路。使用模板,他们的部分特化很难不陷入困惑。还有所有可能的参数变体,如 const const_C & argconst C & argconst_C & argC & arg 看起来不漂亮。我真的不知道该怎么办。使用单独的类或 const_casts,每种方式似乎都是错误的。

在这两种情况下,我都应该将不修改指针目标的方法标记为常量吗?或者只是遵循 const 方法本身不改变对象状态的传统路径(const 方法不关心指针目标)。那么在我的例子中,所有方法都是 const,因为类正在对指针建模,因此指针本身是 T * const。但很明显,其中一些修改了指针的目标,而另一些则没有。

最佳答案

听起来你想要一个可以包装 int*(然后表现为非常量)或 int const*(然后表现为 const)的对象.你不能真正用一个类来正确地完成它。

事实上,const 应用于您的类的想法应该像那样改变它的语义是错误的 - 如果您的类模拟指针或迭代器(如果它包装了一个指针,它很可能是这种情况),那么应用于它的 const 应该只意味着它不能自己改变,并且不应该暗示任何关于指向的值。您应该考虑遵循 STL 为其容器所做的工作 - 这正是它具有不同的 iteratorconst_iterator 类的原因,两者都是不同的,但前者可以隐式转换为后者.同样,在 STL 中,const iteratorconst_iterator 不同!因此,只需执行相同的操作即可。

[EDIT] 这里有一个巧妙的方法,可以最大限度地重用 Cconst_C 之间的代码,同时确保整个过程中的 const 正确性,而不是深入研究U.B. (使用 const_cast):

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++ - 如何处理const对象中非const引用成员的初始化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1699307/

相关文章:

c++ - 为什么我在将 ‘float**’ 转换为 ‘const float**’ 时出错?

c++ - QSvgGenerator 在哪些单元中运行?

c++ - 如果我使用 Array 而不是 Vector,有什么缺点吗?

c++ - boost::unit_test 案例失败,因为子进程以非零值退出

java - 创建对象并在构造函数中使用它

c++ - 使用函数尝试 block 从构造函数中抛出两次异常

c++ - 工厂方法创建 shared_ptr 对象

c# - 在类声明中初始化对象与构造函数的区别

c++ - 在 const 对象上调用非常量函数

c++ - 常量正确性与非变异集合的混淆