Given an immutable C++ object (its members are const, which means its operator=() method does not exist), how do you implement this simple procedural pattern (which would require Object::operator=() to exist):
Object someObject {17};
// ...
if (...) {
someObject = Object {42};
}
// ...
use(someObject);
A workaround is to use shared_ptr.
shared_ptr<Object> someObject(new Object(17));
shared_ptr<Object> anotherObject(new Object(42));
// ...
if (...) {
someObject = anotherObject;
}
use(someObject);
My pattern in such a situation is to extract initialization code into a function:
Object ini(...){
if(...) {
return Object{42};
}
return Object{17};
}
.....
Object someObject=ini(...);// copy constructor used (or not, because of RVO)
use(someObject);
If initialization is simple you could use:
Object someObject = ...? Object{42} : Object{17};
It is not very different to declaring your o-variable const.
If the someObject=17 is used and then replaced with someObject=42 - it's just undermining the good intentions which were pursued by declaring some members const.
There are two alternatives:
declaring some members const was not such bright idea - it can be undone and an assigment operator can be added.
using Object as it was meant to be used.
What should not be done lightly: making some tricks with pointers/references - it will just make your code more complex as it is. Better to use a new variable if needed:
Object someObject {17};
// ...
Object newSomeObject = ... ? Object {42} : someObject {17};
use(newSomeObject);
In the case that copying of the old object could be a performance problem, the code can be refactored in such a way, that
use(need42(..) ? create42() : object17);
can be used without copying data. This solution assumes that use uses a const reference of its argument or the parameter is passed by-value.
In my opinion every change of an immutable object should yield a new object, otherwise, the following can happen:
ImmutableObject obj(1);
ImmutableObject &ref=obj;//ref.member=1
...
obj=ImmutableObject(2);
//ref.member!=1, that is puzzling, I assumed ref to be immutable!
Now, the user of your object (via ref) gets irritated because the object was changed! The whole point of the immutability is that you can reason, that the values never ever change. If they can change, there is not that many advantages in using "immutables" in the first place.
The other answers work when your initialization logic may be simple, but if you're detangling some spaghetti code, this might help.
If it isn't, take from java (they might not have invented it, but I see java programmers use this most) -- the builder pattern. Here are two possible ways of implementing it in C++.
#include <string>
class ImmutableClass {
public:
ImmutableClass(int a, int b, std::string c) : a_(a), b_(b), c_(c) {}
// Getters...
private:
ImmutableClass& operator=(const ImmutableClass&) = delete;
const int& GetA() {return a_;}
const int& GetB() {return b_;}
const std::string& GetC() {return c_;}
const int a_;
const int b_;
const std::string c_;
};
struct ImmutableClassBuilderExampleOne {
public:
// Note the default initialization to avoid undefined behavior.
int a = 0;
int b = 0;
std::string c;
};
// Less boilerplate, less encapsulation, if that's your thing.
ImmutableClass BuildImmutableClass(const ImmutableClassBuilderExampleOne& icb) {
return ImmutableClass(icb.a, icb.b, icb.c);
}
// More boilerplate, more encapsulation, can be "tidied" with macros.
class ImmutableClassBuilderExampleTwo {
public:
const ImmutableClass build() {
return ImmutableClass(a_, b_, c_);
}
ImmutableClassBuilderExampleTwo& setA(const int a) {
a_ = a;
return *this;
}
ImmutableClassBuilderExampleTwo& setB(const int b) {
b_ = b;
return *this;
}
ImmutableClassBuilderExampleTwo& setC(const std::string& c) {
c_ = c;
return *this;
}
private:
// Note the default initialization to avoid undefined behavior.
int a_ = 0;
int b_ = 0;
std::string c_;
};
Related
I'm working with a C++ project and I need to do some assignment code to assign one object to another with a different type like this:
MyClass1 o1;
MyClass2 o2;
o2 = o1;
Ofc, we can make this work with the help of a copy assignment operator of MyClass2: MyClass2& operator=(const MyClass1&).
But this gonna be a very heavy job for me because there has been thousands of classes, which need to do the assignment like o2 = o1. I don't want to add a copy assignment operator for each of them one by one...
I'm thinking if there is some other way, such as some TMP method to help me...
I can ensure that MyClass1 and MyClass2 have exactly the same data members with the same declaration order (see below). If so, is there some TMP method, which could help me?
struct MyClass1 {
int a;
char ch;
std::string msg;
// some virtual member functions
};
struct MyClass2 {
int a;
char ch;
std::string msg;
// some virtual member functions
};
BTW, you may want to ask why there are such two classes/structs with the same data members. Well, this is about some historical reason, I can't fusion them onto one class/struct.
UPDATE
It seems that I didn't make my question clear. I'll make an example here.
void doJob(const MyClass1& o1) {}
void func1(MyClass1 o1) {
doJob(o1);
}
void func2(MyClass2 o2) {
MyClass o1;
o1.? = o2.?; // assign each element of o2 to o1.
doJob(o1);
}
Here is the real case. As you see, o1.? = o2.? contains multi lines, it depends on how many data members of MyClass1/MyClass2. I'm trying to find some way to avoid this stupid assignment of all data members one by one in the func2.
Also, as I said, I have thousands of classes like MyClass1/MyClass2, meaning that these classes have totally different data members.
So for MyClass1 and MyClass2, o1.? = o2.? is o1.a = o2.a; o1.ch = o2.ch; o1.msg = o2.msg; But for other classes, it may become o1.f = o2.f; o1.vec = o2.vec;. That's why I'm thinking I may need some TMP technique...
UPDATE2
Alice developed the classes:
struct MyClass1 {// data members};
struct MyClass2 {// data members};
// MyClass1 and MyClass2 have exactly the same data members and declaration order
struct MyClass3 {// data members};
struct MyClass4 {// data members};
// MyClass3 and MyClass4 have exactly the same data members and declaration order
...
...
struct MyClass1000 {// data members};
struct MyClass1001 {// data members};
// MyClass1000 and MyClass1001 have exactly the same data members and declaration order
I'm developing the functions:
void doJob1(const MyClass1& o1) {}
void func1(MyClass1 o1) {
doJob(o1);
}
void func2(MyClass2 o2) {
MyClass1 o1;
o1.? = o2.?; // assign each element of o2 to o1.
doJob1(o1);
}
...
...
void doJob1000(const MyClass1000& o1) {}
void func1000(MyClass1000 o1) {
doJob1000(o1);
}
void func1001(MyClass1001 o2) {
MyClass1000 o1;
o1.? = o2.?; // assign each element of o2 to o1.
doJob1000(o1);
}
Why did Alice do such a stupid design? For some historical reason...
Why not using std::memcpy? Because these classes contain virtual functions.
yes, you can... but I is not recommended unless the two classes have an exact one to one correspondence and the exact same meaning in the domain of your problem.
Why is not recommended?
Because the operation needs to be manually implemented (the compiler cannot help with a = default declaration).
auto operator=(MyClass1 const& other) -> MyClass2& {
std::tie(a, ch, msg) = std::tie(other.a, other.ch, other.msg);
return *this;
}
Because if you defined assignment, you will eventually need to define equality (==), and inequality (!=) and in both directions. Otherwise the clases will not behave logically.
bool operator==(MyClass1 const& mc1, MyClass2 const& mc2) {
return std::tie(mc1.a, mc1.ch, mc1.msg) == std::tie(mc2.a, mc2.ch, mc2.msg);
}
bool operator==(MyClass2 const& mc2, MyClass1 const& mc1) {
return std::tie(mc1.a, mc1.ch, mc1.msg) == std::tie(mc2.a, mc2.ch, mc2.msg);
}
bool operator!=(MyClass1 const& mc1, MyClass2 const& mc2) {
return std::tie(mc1.a, mc1.ch, mc1.msg) != std::tie(mc2.a, mc2.ch, mc2.msg);
}
bool operator!=(MyClass2 const& mc2, MyClass1 const& mc1) {
return std::tie(mc1.a, mc1.ch, mc1.msg) != std::tie(mc2.a, mc2.ch, mc2.msg);
}
Because if you define assignment, you will to define a constructor or a conversion from one to the other.
/*explicit?*/ operator MyClass1() const& {return {a, ch, msg};} // need thios
and a move constructor?
/*explicit?*/ operator MyClass1() && {return {a, ch, std::move(msg)};} // need this
if one can be ordered the other also, and you will need to define order between the two classes, in both directions
// won't even try
// bool operator<
// bool operator<=
// bool operator>
// bool operator>=
// bool operator<
// bool operator<=
// bool operator>
// bool operator>=
and for that matter any function that work with one should work with the other, because well, when you assign you are saying that two things are logical equal among other tings.
full code here: https://godbolt.org/z/c8d34eT48
While it seems to be your case (albeit a very suspicious case), as you see, you open a Pandora's box by defining equality between two classes.
Just by calling the "assignment" convert instead you save your self a big headache. Don't use operator=. My recommended code is to just use another name:
MyClass2& convert(MyClass1 const& from, MyClass2& to) {
std::tie(to.a, to.ch, to.msg) = std::tie(from.a, from.ch, from.msg);
return to;
}
MyClass2& convert(MyClass1&& from, MyClass2& to) {
std::tie(to.a, to.ch, to.msg) = std::tie(from.a, from.ch, std::move(from.msg));
return to;
}
More material: https://www.youtube.com/watch?v=ABkxMSbejZI
Once you understand the drawbacks, std::tie can help you (as shown).
Also, if all classes are public and simple, Boost.PFR https://www.boost.org/doc/libs/1_78_0/doc/html/boost_pfr.html
struct iClass
{
int a;
char ch;
std::string msg;
// implement iClass == = operator
};
struct MyClass1 : virtual iClass{
// some virtual member functions
};
struct MyClass2 : virtual iClass {
// some virtual member functions
};
Should be able to compare by reintrepret_cast to iClass as well assignment.
Suppose I have a class that has an array of pointers, and I have a method that dereferences a pointer and returns it as a reference. I want to allow the method caller to call non-const methods of the object the pointer is pointing to, but also want to protect myself from the caller changing what the pointer is pointing to. If I return a const reference, I have to mark many of the pointer object's methods as const, and hence many of its class member variables as mutable.
Is this bad practice? If so, how do I get around this?
Is there performance penalty for over-using mutable?
Example:
#include <iostream>
#include <array>
#include <memory>
class Counter
{
public:
Counter();
void hit() const;
void reset();
unsigned count() const;
private:
mutable unsigned count_;
};
Counter::Counter() : count_(0) {}
void Counter::hit() const { ++count_; }
void Counter::reset() { count_ = 0; }
unsigned Counter::count() const { return count_; }
class CircularArray
{
public:
CircularArray();
const Counter& next() const;
private:
mutable unsigned i_;
std::array<std::unique_ptr<Counter>, 3> arr_;
};
CircularArray::CircularArray() : i_(2)
{
arr_[0] = std::unique_ptr<Counter>(new Counter);
arr_[1] = std::unique_ptr<Counter>(new Counter);
arr_[2] = std::unique_ptr<Counter>(new Counter);
}
const Counter& CircularArray::next() const { return *arr_[(i_ = (i_ + 1) % 3)]; }
int main()
{
CircularArray circular;
const Counter* p;
p = &circular.next();
p->hit();
p->hit();
Counter c;
//*p = c; // <-- Want to prevent this
}
To extend what I was saying, there is no point in abusing mutable for this. If this is all you want to prevent:
*p = /* ... */;
then it can be done much more easily by deleting the assignment operator of Counter:
class Counter
{
void operator=(const Counter&) = delete;
// ...
};
Remember that the assignment operator does not affect the identity of the object: it doesn't change its address. Semantically, an assignment involving of modifying this object to replicate the state of another object. In fact, even if you forbid me from using the assignment operator somehow, I could still do this:
// a very inefficient way of performing `*p = c`
p->reset();
while (p->count() != c.count())
p->hit();
This achieves the exact same result as performing an assignment, albeit very clumsily and inefficiently.
Performing an assignment is no different than calling a non-const member function that accepts a single argument of type const Counter&. Hypothetically, you could redefine the assignment operator to do absolutely nothing at all if you wanted to (it would be a bad idea though).
Let's assume you are writing a small library or an API that will be distributed to other programes,
that means you never know how other programers will create objects: will such an object be const, volatile, const volatile or it will be just an plain object.
normaly when we declare some class we do it so:
class A // THIS CLASS DOES NOT SUPPORT ANYTHING
{
public:
int get() { return x; }
private:
int x;
};
however if you want your class to support const objects you'll overload member function with const qualificator:
class B // THIS CLASS SUPPORTS CONST OBJECTS
{
public:
int get() { return x; }
int get() const { return x; }
private:
mutable int x;
};
more futer maybe we want also to support volatile but not const for our class:
class C // THIS CLASS SUPPORTS VOLATILE OBJECTS
{
public:
int get() { return x; }
int get() volatile { return x; }
private:
int x;
};
BUT what if user will use a object that is const or volatile or
what if user will use a object that is volatile and const in same time?
then we should add support for that too!
class D // THIS CLASS SUPPORTS CONST, VOLATILE AND CONST VOLATILE OBJECTS
{
public:
int get() { return x; }
int get() const { return x; }
int get() volatile { return x; }
int get() const volatile { return x; }
private:
mutable int x;
};
now let's see why would we want our class to have those 4 overloads:
// EXAMPLE
int main()
{
// figure 1
const A a ;
a.get(); // ERROR
// figure 2
volatile B b;
b.get(); // ERROR
// figure 3
const volatile C c;
c.get(); // ERROR
// figure 4 where we finaly created a super class capable of anything!!
const volatile D d;
d.get(); // NOW IS OK!
cin.ignore();
return 0;
}
well in this last example (figure 4) we can be shore that our class is capable to be instatated of any type
that means other programers will not have problem to create volatile, const or volatile const object of your class!
my question is:
is it a good design practice to overlaod each method four times?
if not why not?
also if our class has let's say 20 methods then it will acctualy have 80 methods when you overload them all!
EDIT:
does real world API classes do such thing?
if not, then how would we create volatile or const volatile object of that class if let's say we have such need.
My opinion is that in the real world:
Volatile is not widely used, and when it is it modifies always basic types. But never objects, so you never need to override a volatile member function.
const should be added to the member functions, not thinking in whether it will be needed by a const client code, but think instead if the operation the member function does is conceptually constant. That is called const correctness:
a. First of all, the function should do just one thing. Maybe one complex thing, but describable as a single concept.
b. Then, ask yourself whether this thing the function does changes the observable state of the object. If it does, then the function should not be constant. And if it does not, then declare it as constant.
When a client code wants to use your class, it will have a const reference when it is not allowed to modify the state of the object, and all will just work.
Note that I talked about the observable state of the object, not the actual contents of the member variables: that is an implementation detail.
Edit1: I realize this is hard to understand this question without having an insight of what I'm trying to do. The class A is not complete but it essentially stand for a C-array "proxy" (or "viewer" or "sampler"). One interesting usage is too present a C-array as a 2d grid (the relevant function are not shown here). The property of this class are the following:
it should not own the data - no deep copyy
it should be copyable/assignable
it should be lightweight (
it should preserve constness (I'm having trouble with this one)
Please do not question the purpose or the design: they are the hypothesis of the question.
First some code:
class A
{
private:
float* m_pt;
public:
A(float* pt)
:m_pt(pt)
{}
const float* get() const
{
return m_pt;
}
void set(float pt)
{
*m_pt = pt;
}
};
void gfi()
{
float value = 1.0f;
const A ac(&value);
std::cout<<(*ac.get())<<std::endl;
A a = ac;
a.set(2.0f);
std::cout<<(*ac.get())<<std::endl;
}
Calling "gfi" generate the following output:
1
2
Assigning a with ac is a cheap way to shortcut the constness of ac.
Is there a better way to protect the value which m_pt point at?
Note that I DO want my class to be copyable/assignable, I just don't want it to loose its constness in the process.
Edit0: I also DO want to have a pointer in there, and no deep copy please (let say the pointer can be a gigantic array).
Edit2: thanks to the answers, I came to the conclusion that a "const constructor" would be a useful thing to have (at least in this context). I looked it up and of course I'm not the same one who reached this conclusion. Here's an interesting discussion:
http://www.rhinocerus.net/forum/language-c-moderated/569757-const-constructor.html
Edit3: Finally got something which I'm happy with. Thanks for your help. Further feedback is more than welcome
template<typename T>
class proxy
{
public:
typedef T elem_t;
typedef typename boost::remove_const<T>::type elem_unconst_t;
typedef typename boost::add_const<T>::type elem_const_t;
public:
elem_t* m_data;
public:
proxy(elem_t* data = 0)
:m_data(data)
{}
operator proxy<elem_const_t>()
{
return proxy<elem_const_t>(m_data);
}
}; // end of class proxy
void test()
{
int i = 3;
proxy<int> a(&i);
proxy<int> b(&i);
proxy<const int> ac(&i);
proxy<const int> bc(&i);
proxy<const int> cc = a;
a=b;
ac=bc;
ac=a;
//a=ac; // error C2679: binary '=' : no operator found which takes a right-hand operand of type...
//ac.m_data[0]=2; // error C3892: 'ac' : you cannot assign to a variable that is const
a.m_data[0]=2;
}
Your class is badly designed:
it should use float values, not pointers
if you want to use pointers, you probably need to allocate them dynamically
and then you need to give your class a copy constructor and assignment operator (and a destructor) , which will solve the problem
Alternatively, you should prevent copying and assignment by making the copy constructor and assignment op private and then not implementing them.
You can trick around with proxy pattern and additional run-time constness boolean member. But first, please tell us why.
Effectively your class is like an iterator that can only see one value. It does not encapsulate your data just points to it.
The problem you are facing has been solved for iterators you should read some documentation on creating your own iterator and const_iterator pairs to see how to do this.
Note: in general a const iterator is an iterator that cannot be incremented/decremented but can change the value it points to. Where as a const_iterator is a different class that can be incremented/decremented but the value it points to cannot be changed.
This is the same as the difference between const float * and float *const. In your case A is the same as float * and const A is the same as float *const.
To me your choices seem to be:
Encapsulate your data.
Create a separate const_A class like iterators do
Create your own copy constructor that does not allow copies of const A eg with a signature of A(A & a);
EDIT: considering this question some more, I think you are misinterpreting the effect of const-correctness on member pointers. Consider the following surprising example:
//--------------------------------------------------------------------------------
class CNotSoConstPointer
{
float *mp_value;
public:
CNotSoConstPointer(float *ip_value) : mp_value(ip_value) {}
void ModifyWithConst(float i_value) const
{
mp_value[0] = i_value;
}
float GetValue() const
{
return mp_value[0];
}
};
//--------------------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[])
{
float value = 12;
const CNotSoConstPointer b(&value);
std::cout << b.GetValue() << std::endl;
b.ModifyWithConst(15);
std::cout << b.GetValue() << std::endl;
while(!_kbhit()) {}
return 0;
}
This will output 12 and then 15, without ever being "clever" about the const-correctness of the const not-so-const object. The reason is that only the pointer ITSELF is const, not the memory it points to.
If the latter is what you want, you'll need a lot more wrapping to get the behavior you want, like in my original suggestion below or Iain suggestion.
ORIGINAL ANSWER:
You could create a template for your array-proxy, specialized on const-arrays for the const version. The specialized version would have a const *m_pt, return a const pointer, throw an error when you try to set, and so on.
Edit: Something like this:
template<typename T>
class TProxy
{
T m_value;
public:
TProxy(T i_t) : m_value(i_t) {};
template<typename T>
TProxy(const TProxy<T> &i_rhs) : m_value(i_rhs.m_value) {}
T get() { return m_value; }
void set(T i_t) { m_value = i_t; }
};
template<typename T>
class TProxy<const T *>
{
const T *mp_value;
public:
TProxy(const T *ip_t) : mp_value(ip_t) {};
template<typename T>
TProxy(const TProxy<T> &i_rhs) : m_value(i_rhs.mp_value) {}
T get() { return m_value; }
};
Why not replace float* with float in A. If you don't either the original owner of the float that the float* references can change it, or anyone prepared to do a mutable cast on the return value from a::get.
const is always just a hint to the compiler; there are no ways to make a variable permanently read-only.
I think you should use deep copy and define your own assingment operator and copy constructor.
Also to return handle to internal data structure in not a good practice.
You can deny the copy-constructor for certain combinations of arguments:
For instance, adding the constructor;
A(A& a) :m_pt(a.m_pt) { m_pt = a.m_pt; }
prevents any instance of A being initialised with a const A.
This also prevents const A a2 = a1 where a1 is const, but you should never need to do this anyway, since you can just use a1 directly - it's const even if you could make a copy, a2 would be forever identical to a1.
I want to return two values, one of which is a new object. I can do this using std::pair:
class A {
//...
};
std::pair<A*, int> getA()
{
A* a = new A;
//...
}
To make the code exception-safe, I would like to do:
std::pair<std::auto_ptr<A>, int> getA()
{
std::auto_ptr<A> a(new A);
//...
}
But this won't compile as the auto_ptr cannot be copied without modifying the auto_ptr being copied. Ok, this means auto_ptr does not compose well like other types (in one more way). What is a good way of returning a new object in this case?
One alternative is to return a shared_ptr and another is an inout reference. But I am looking for some other alternative. I can do something like:
class AGetter
{
void getAExecute()
{
//...
// set a_ and i_
}
std::auto_ptr<A> getA() const
{
return a_.release();
}
int getInt() const
{
return i_;
}
private:
std::auto_ptr<A> a_;
int i_;
};
Is there a better way?
There are two major ways to handle this problem:
Do the cleanup yourself, via try/catch.
Use some kind of automated memory management, like shared_ptr.
auto_ptr doesn't really work in these sorts of cases, as you discovered, but the new standard and Boost both contain reference-counted pointers that do what you want. Let us know what you don't like about shared_ptr, and maybe we can suggest an alternative.
A shared_ptr would be ideal in that situation, but if you really don't want to use those you could return an auto_ptr to a pair containing the object and the int instead.
Just create a new class and return that class
class Result
{
A* a;
int i;
public:
Result( A*a, int i ) : a(a), i(i) {
}
~Result() {
delete a;
}
// access functions, copy constructor, ...
};
Result getA() {
//...
return Result(new A, intValue);
}
It's not pretty, but you could return the other value through a pointer or reference argument:
int i;
std::auto_ptr<A> a = getA(&i);
Why not through two reference parameters?
class AGetter
{
// ..
void get(std::auto_ptr<A>& a, int& i)
{
a = a_.release();
i = i_;
}
// ..
};