简体   繁体   English

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

[英]Why does C++ not have a const constructor?

( Edit: Heavy change because previous example was flawed, which may make some answers/comments seem odd) 编辑:重大更改,因为先前的示例有缺陷,这可能使某些答案/评论显得有些奇怪)

This might be an overly contrived, but the following is legal because of lack of const constructor: 这可能是一个过分的设计,但是由于缺少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;
}

It seems like providing a const constructor a thing would be as easy technically as const methods, and be analogous to a const overload. 看起来,提供const构造函数在技术上和const方法一样容易,并且类似于const重载。

Note: I'm not looking for ' Image( const Data & data ) const ' but rather ' const Image( const Data & data) const ' 注意:我不是在寻找' Image( const Data & data ) const '而是' const Image( const Data & data) const '

So: 所以:

  • Why is the const constructor absent in C++? 为什么C ++中没有const构造函数?

Here's some related material for context: 以下是一些有关上下文的材料:

Just because Image is const in your imaginary constructor doesn't mean that what m_data points to is. 仅仅因为Image在您虚构的构造const中为const ,并不意味着m_data指向的是。 You'd end up being able to assign a "pointer to const" to a "const pointer to non-const" inside your class, which would remove constness without a cast. 您最终将能够在类内部将“指向const的指针”分配给“指向非const的const指针”,这将删除constness而无需强制转换。 This would obviously allow you to violate invariants and couldn't be allowed. 显然,这将允许您违反不变式,并且不允许这样做。

As far as I know, any specific sets of const-ness that are needed can be accurately and completely specified within the current standard. 据我所知,可以在当前标准中准确,完整地指定所需的任何特定的常数集。

Another way to look at it is that const means the method doesn't mutate your object's state. 另一种看待它的方式是const表示该方法不会改变对象的状态。 A constructor's sole purpose is to initialize an object's state to be valid (well hopefully anyway - any constructors with side effects should be ...carefully evaluated). 构造函数的唯一目的是将对象的状态初始化为有效(希望无论如何-应当仔细评估具有副作用的任何构造函数)。

EDIT: In C++ constness applies to both members, and for pointers and references, to the accessible constness of the referred object. 编辑:在C ++中,常量性适用于两个成员,并且对于指针和引用,适用于所引用对象的可访问常量。 C++ consciously made the decision to split out these two different const-nesses. C ++自觉地决定将这两个不同的常量分开。 Firstly, do we agree that this code demonstrating the difference should compile and print out "non-const"? 首先,我们是否同意该演示差异的代码应编译并打印出“非常量”?

#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;
}

So then in order to obtain the behavior where it could accept a const-ref and store it as const-ref, the effective declaration of the reference would have to change to be const. 因此,为了获得可以接受const-ref并将其存储为const-ref的行为,必须将引用的有效声明更改为const。 This would then mean that it would be a completely distinct type, NOT a const version of the original type (since two types with members differing in const-qualification are treated as two separate types in C++). 然后,这将意味着它将是一个完全不同的类型,而不是原始类型的const版本(因为在const中,成员类型不同的两种类型在C ++中被视为两种单独的类型)。 Thus, either the compiler has to be able to do excessive behind-the-scenes magic to convert these things back and forth, remembering the const-ness of the members, or it has to treat it as a separate type which then couldn't be used in place of the normal type. 因此,编译器要么必须能够做大量的幕后魔术来来回转换这些东西,记住成员的常数,要么就必须将其视为一个单独的类型,而后又不能代替普通类型使用。

I think what you're trying to achieve is a referencee_const object, a concept that only exists in C++ as a separate class (which I suspect could be implemented with judicious use of templates although I didn't make an attempt). 我认为您想要实现的是referencee_const对象,该概念仅在C ++中作为单独的类存在(我怀疑可以通过谨慎使用模板来实现,尽管我没有尝试过)。

Is this strictly a theoretical question (answer: C++ decided to split object and referencee constness) or is there an actual practical uncontrived problem you're trying to solve? 严格来说,这是理论上的问题吗(答案:C ++决定拆分对象和引用常量),或者您要解决的是实际的实际人为问题?

It would not be a const method itself 它本身不是const方法

If this constructor were not a const method itself, then the internal pointers and such would also not be const . 如果此构造方法本身不是const方法,则内部指针等也将不是const Therefore, it could not set const values into those non- const members. 因此,它无法将const值设置为这些非const成员。

The only way to make it work syntactically is for this constructor to require member initialization for all non- mutable members. 进行的唯一方式,语法,工作是这个构造要求成员初始化所有非mutable成员。 Essentially, any member not declared mutable would be implicitly declared const when using this constructor. 本质上,使用此构造const时,任何未声明为mutable成员都将隐式声明为const Which is equivalent to making the constructor a const method; 这相当于使构造函数成为const方法; only initializers could initialize members. 只有初始化程序才能初始化成员。 The constructor's body could do nothing with non-mutable members, because those members would be const at that point. 构造函数的主体不能对非可变成员执行任何操作,因为在那一点上那些成员将是const

What you are asking for is syntactically dubious. 您要的是语法上可疑的。 You're essentially trying to hoodwink the API, storing constant data in an object that is designed for mutable data (which is why you didn't declare the member pointer to be const ). 本质上,您试图欺骗API,将常量数据存储在旨在用于可变数据的对象中(这就是为什么未将成员指针声明为const )。 If you want different behavior for an object, you need to declare the object to have that specific behavior. 如果您希望对象具有不同的行为,则需要声明该对象具有该特定行为。

Mark B goes over the fundamental considerations, but note that you can do something similar in pure C++. Mark B超越了基本考虑因素,但请注意,您可以在纯C ++中执行类似的操作。 Consider: 考虑:

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

Instead of using const Image * , use ConstImage * , and there you go. 而不是使用const Image * ,而是使用ConstImage * ,然后就可以了。 You could also simply define a static function pseudo-constructor: 您还可以简单地定义一个静态函数伪构造函数:

const Image *Image::newConstImage(const Data *d) {
  return new Image(const_cast<Data*>(d));
}

This, of course, relies on the programmer to ensure that there aren't any const functions which might somehow mutate the pointed-to Data 's state. 当然,这依赖于程序员来确保不存在任何可能以某种方式使指向Data的状态发生变化的const函数。

You can also combine these techniques: 您还可以结合使用以下技术:

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

This gets the best of both worlds; 这是两全其美的。 since data() is a non-const member, you have static checking for mutation of the pointed-to value. 由于data()是非常量成员,因此您需要对指向的值进行静态检查。 You also, however, have a const constructor, and can directly cast between Image * and const Image * (ie, you can remove the constness if you know it is safe). 但是,您也具有const构造函数,并且可以直接在Image *const Image * (即,如果知道安全性,则可以删除const Image * )。

You can also abstract away the separation of pointers further: 您还可以进一步抽象指针的分离:

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

Now, if this is const, data becomes const, propagating that into *data as well. 现在,如果this是const,则data变为const,并将其也传播为*data Good enough for you? 对你足够好吗? :) :)

I suppose the final answer is probably this: In order for a const constructor to be useful and safe, we'd need something like the ConstPropPointer you see there built into the language. 我想最终的答案可能是这样的:为了使const构造函数有用且安全,我们需要在语言中内置类似ConstPropPointer类的东西。 Const constructors would then be allowed to assign from const T * to constprop T * . 然后,将允许const构造函数将const T *分配给constprop T * This is more complex than it sounds - for example, how does this interact with template classes such as vector ? 这比听起来要复杂得多,例如,这如何与模板类(例如vector相互作用?

So, this is a somewhat complex change, but the problem doesn't seem to come up all that much. 因此,这是一个有点复杂的更改,但是问题似乎并没有解决的那么多。 More importantly, there's a simple workaround here (the ConstPropPointer can be librarized, and the static pseudo-constructor is simple enough to add). 更重要的是,这里有一个简单的解决方法( ConstPropPointer可以被解放,而静态伪构造函数很容易添加)。 So the C++ committee probably passed it over for more important things, if it was even proposed at all. 因此,C ++委员会可能甚至将其移交给更重要的事情,甚至根本没有提出。

In my opinion, the fact of that the ctors haven't return-type specification is what fails here. 在我看来,ctor没有返回类型说明这一事实在这里是失败的。 Any other imaginable syntax like for example 任何其他可以想象的语法,例如

class A
{
    const A& ctor(...);
}

would be, imho, very valuable. 恕我直言,这将是非常有价值的。 For example, imagine such a situation of calling a method with prototype 例如,想象一下用原型调用方法的情况

void my_method(const my_own_string_class& z);

If my_own_string_class holds a ctor from char*, the compiler could choose this ctor, but as this ctor is not allowed to return a const object, it need to allocate and copy... If const return type were allowed, one could do 如果my_own_string_class拥有char *中的ctor,则编译器可以选择此ctor,但是由于不允许该ctor返回const对象,因此需要分配和复制...如果允许const返回类型,则可以执行此操作

class my_own_string_class
{
    char *_ptr;
    public:
    const my_own_string_class& ctor(char *txt)
    : _ptr(txt)
    { return *this;}
 }

provided that this special construct be restricted to the creation of temporal instances. 前提是该特殊构造仅限于时间实例的创建。 (And dtor's must be mutable ;) ). (并且dtor必须是可变的;))。

const对象应该初始化其成员变量,而const构造函数将无法这样做。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM