Why does C++ not have a const constructor?

后端 未结 5 682
生来不讨喜
生来不讨喜 2020-11-29 06:23

(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

5条回答
  •  情书的邮戳
    2020-11-29 07:03

    Mark B goes over the fundamental considerations, but note that you can do something similar in pure 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(const_data); }
    public:
      Image(Data *d) : const_data(d) { }
      void frob() { data()->frob(); }
    };
    

    Instead of using const Image *, use ConstImage *, and there you go. You could also simply define a static function pseudo-constructor:

    const Image *Image::newConstImage(const Data *d) {
      return new Image(const_cast(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.

    You can also combine these techniques:

    class Image {
    protected:
      const Data *const_data;
      Data *data() { return const_cast(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(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. 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).

    You can also abstract away the separation of pointers further:

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

    Now, if this is const, data becomes const, propagating that into *data as well. 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 constructors would then be allowed to assign from const T * to constprop T *. This is more complex than it sounds - for example, how does this interact with template classes such as 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). So the C++ committee probably passed it over for more important things, if it was even proposed at all.

提交回复
热议问题