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, e.g. 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.
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 :/
}
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.