简体   繁体   中英

Replace a variable of a type with const members

Suppose I have a class with some constant member:

class MyClass {
public:
   MyClass(int a) : a(a) {
   }
   MyClass() : MyClass(0) {
   }
   ~MyClass() {
   }
   const int a;
};

Now I want to store an instance of MyClass somewhere, eg as a global variable or as an attribute of another object.

MyClass var;

Later, I want to assign a value to var :

var = MyClass(5);

Obviously, this does not work, because the assign operator is invoked, which does not exist by default, because MyClass has a const attribute. So far so good.

My question is, how can I assign a value to var anyway? After all, var itself is not declared to be a constant.

My thoughts so far

I know that the problem does not exist if I use a pointer for var :

MyClass *var;
var = new MyClass(5);

However, I would not like to use a pointer for convenience reasons.

A potential solution is to overwrite the memory with placement new:

template<class T, class... Args>
T &emplaceVar(T &myVar, Args&&... args) {
   myVar.~T(); // free internal memory
   return *new (&myVar) T(args...);
}

emplaceVar(var, 5);

This would solve the problem, but I am not sure if this may cause memory leaks or any other issues I have not thought of due to my lack of experience in c++. Furthermore, I would have thought there must be an easier way. Is there?

const members are problematic in general for the very reason you discovered.

The much simpler alternative is to make the member private and take care to provide no means to modify it from outside the class:

class MyClass {
public:
   MyClass(int a) : a(a) {
   }
   MyClass() : MyClass(0) {
   }
   ~MyClass() {
   }
private:
   int a;
};

I did not add a getter yet, because you say access via myObject.a is a hard requirement. Enabling this requires a bit of boilerplate, but it is much less hacky than modifiying something that must not be modified:

class MyClass {
public:
   struct property {
       const int& value;
       operator int(){ return value;}
       property(const property&) = delete;
   };

   MyClass(int a = 0) : value(a) {}
private:
   int value;
public:
    property a{value};
};

int main(){
    MyClass myObject{5};
    int x = myObject.a;
    //myObject.a = 42; // error
    //auto y = myObject.a; // unexpected type :/
}

Live Demo

Drawback is that it does not play well with auto . If by any means you can accept myObject.a() I would suggest to use that and keep it simple.

how can I assign a value to var anyway?

You can do that with a user-defined assignment operator:

class MyClass {
public:
   MyClass &operator=(const MyClass &o)
   {
         // Implement your assignment here

         return *this;
   }

   // ...
};

Your assignment operator can do anything that any operator= overload can. The only thing it can't do is assign anything to its const class member. That's because it's constant.

If a class does not have user-defined assignment operator, the default assignment operator assigns each member of the assigned-to object from the same member of the assigned-from object. However the default assignment operator is deleted from any class that has a const member, because that, of course, is no longer possible.

In your user-defined operator you can do whatever it means to assign one of these objects from another one. The only thing it can't do is the same thing any other class method can't do: modify a const class member.

You mentioned manual invocation of a destructor and placement new. That's possible, provided that all requisite requirements are met and undefined behavior is carefully avoided. However, technically, it wouldn't be assignment, but rather a manual destruction and construction of another object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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