为什么C++没有const构造函数?

Cat*_*kul 35 c++ constructor const

(编辑:重大改变,因为前面的例子是有缺陷的,这可能会使一些答案/评论看起来很奇怪)

这可能是一个过于设计,但由于缺少const构造函数,以下是合法的:

class Cheater
{
public:
    Cheater(int avalue) 
       : cheaterPtr(this) //conceptually odd legality in const Cheater ctor
       , value(avalue) 
    {}

    Cheater& getCheaterPtr() const {return *cheaterPtr;}
    int value;

private:
    Cheater * cheaterPtr;
};

int main()
{
    const Cheater cheater(7); //Initialize the value to 7

    cheater.value                 = 4;    //good, illegal
    cheater.getCheaterPtr().value = 4;    //oops, legal

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

似乎提供const构造函数在技术上就像const方法一样容易,并且类似于const重载.

注意:我不是在寻找' Image( const Data & data ) const'而是' const Image( const Data & data) const'

所以:

  • 为什么const构造函数在C++中不存在?

这是上下文的一些相关材料:

Mar*_*k B 10

正因为Imageconst在你想象的构造并不意味着什么m_data指向是.您最终能够将"指向const的指针"分配给类中的"const指向非const",这样可以在没有强制转换的情况下删除const.这显然会允许您违反不变量而且不允许.

据我所知,在当前标准范围内,可以准确,完整地指定所需的任何特定的常数.

另一种看待它const的方法是,该方法不会改变对象的状态.构造函数的唯一目的是将对象的状态初始化为有效(无论如何 - 希望任何具有副作用的构造函数都应该......进行仔细评估).

编辑:在C++中,constness既适用于成员,也适用于指针和引用,适用于引用对象的可访问const.C++有意识地决定拆分这两个不同的常量.首先,我们是否同意这个展示差异的代码应该编译并打印出"非常量"?

#include <iostream>

struct Data
{
    void non_const() { std::cout << "non-const" << std::endl; }
};

struct Image
{
     Image(             Data & data ) : m_data( data ) {}

     void check() const { m_data.non_const(); }
     Data & m_data;
};

int main()
{
    Data data;
    const Image img(data);
    img.check();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

那么为了获得它可以接受const-ref并将其存储为const-ref的行为,引用的有效声明必须改为const.这将意味着它将是一个完全不同的类型,而不是const原始类型的版本(因为具有const-qualification不同成员的两种类型在C++中被视为两个单独的类型).因此,编译器必须能够做过多的幕后魔术来来回转换这些东西,记住成员的常量,或者它必须将它视为一个单独的类型然后不能用于代替普通类型.

我认为你想要实现的是一个referencee_const对象,这个概念只作为一个单独的类存在于C++中(我怀疑可以通过明智地使用模板来实现,尽管我没有尝试).

这是一个严格的理论问题(答案:C++决定拆分对象和引用的常量)还是有一个实际的实际未解决的问题,你试图解决?


Nic*_*las 6

它本身不是const方法

如果这个构造函数const本身不是一个方法,那么内部指针等也不会const.因此,它无法将const值设置为那些非const成员.

使其在语法上工作的唯一方法是此构造函数要求所有非mutable成员的成员初始化.实质上,使用此构造函数时,mutable将隐式声明未声明的任何成员const.这相当于使构造函数成为一个const方法; 只有初始化程序才能初始化成员.构造函数的主体无法对不可变成员执行任何操作,因为那些成员就const在那时.

你所要求的是语法上可疑的.您实际上是在试图欺骗API,将常量数据存储在为可变数据设计的对象中(这就是为什么您没有声明成员指针的原因const).如果您想要对象的不同行为,则需要声明该对象具有该特定行为.

  • 那...不遵循.const构造函数就像一个非const构造函数,唯一的区别是这样构造的对象在构造结束时将是const限定的. (4认同)

bdo*_*lan 5

Mark B 回顾了基本的考虑因素,但请注意,您可以在纯 C++ 中执行类似的操作。考虑:

struct Data { };

class ConstImage {
protected:
  const Data *const_data;
public:
  ConstImage (const Data *cd) : const_data(cd) { }
  int getFoo() const { return const_data->getFoo(); }
};

class Image : public ConstImage {
protected:
  Data *data() { return const_cast<Data *>(const_data); }
public:
  Image(Data *d) : const_data(d) { }
  void frob() { data()->frob(); }
};
Run Code Online (Sandbox Code Playgroud)

不要使用const Image *,而是使用ConstImage *,然后就可以了。您还可以简单地定义一个静态函数伪构造函数:

const Image *Image::newConstImage(const Data *d) {
  return new Image(const_cast<Data*>(d));
}
Run Code Online (Sandbox Code Playgroud)

当然,这依赖于程序员来确保不存在任何const可能以某种方式改变所指向的Data状态的函数。

您还可以结合使用这些技术:

class Image {
protected:
  const Data *const_data;
  Data *data() { return const_cast<Data *>(const_data); }
public:
  void frob() { data()->frob(); }
  int getFoo() const { return const_data->getFoo(); }

  Image(Data *d) : const_data(d) { }

  static const Image *newConst(const Data *cd) {
    return new Image(const_cast<Data *>(cd));
  }
};
Run Code Online (Sandbox Code Playgroud)

这可以两全其美;由于data()是非常量成员,因此您可以静态检查指向值的突变。但是,您也有一个 const 构造函数,并且可以直接在Image *和之间进行转换const Image *(即,如果您知道它是安全的,则可以删除常量)。

您还可以进一步抽象出指针的分离:

template<typename T>
class ConstPropPointer {
private:
  T *ptr;
public:
  ConstPropPointer(T *ptr_) : ptr(ptr_) { }
  T &operator*() { return *ptr; }
  const T &operator*() const { return *ptr; }
  T *operator->() { return ptr; }
  const T *operator->() const { return ptr; }
};


class Image {
protected:
  ConstPropPointer<Data> data;
public:
  void frob() { data->frob(); }
  int getFoo() const { return data->getFoo(); }

  Image(Data *d) : data(d) { }

  static const Image *newConst(const Data *cd) {
    return new Image(const_cast<Data *>(cd));
  }
};
Run Code Online (Sandbox Code Playgroud)

现在,如果this是 const,data则变为 const,并将其传播到其中*data。对你来说足够好了吗?:)

我想最终的答案可能是这样的:为了使 const 构造函数有用且安全,我们需要像ConstPropPointer您在语言中看到的那样的东西。const T *然后,Const 构造函数将被允许从to赋值constprop T *。这比听起来更复杂 - 例如,它如何与诸如 ? 之类的模板类交互vector

所以,这是一个有点复杂的改变,但问题似乎并没有那么严重。更重要的是,这里有一个简单的解决方法(ConstPropPointer可以库化,并且静态伪构造函数很容易添加)。因此,C++ 委员会可能会因为更重要的事情而忽略它,即使它确实被提议过。