Reputation: 166162
I'm trying to create am immutable type (class) in C++,
I made it so that all methods "aka member functions" don't modify the object and return a new instance instead.
I'm running across a bunch of issues, but they all revolve around the reference types in C++.
One example is when passing parameters of the same class type by reference:
Imm Imm::someOp( const Imm& p_im ) const
{
...
p_im = p_im.someOtherOp(); //error, p_im is const, can't modify it!
...
}
The error is caused by passing the value by reference. If instead, I was passing the reference by value, then the error line above would not be an error!
Consider a Java/C# example
class Imm
{
...
Imm someOp( Imm p_im )
{
....
p_im = p_im.someOtherOp(); //ok, you're not modifying the
//original object, just changing the local reference
....
}
....
}
How can I do something like this in C++? I know I can use pointers but then I run into the whole memory management mess. I don't want to worry about who owns references to objects.
Ideally I'd like to design the class to be like immutable strings in python; you can use them without ever noticing or even knowing that they're immutable, and they just behave as you expect; they just work.
EDIT
Of course I can get around it by passing-by-value or by using a temp variable (which is what I'm doing currently). What I'm asking about is "how to pass references by value in C++"
I'm expecting the answer to revolve around something in the STL, I'm currently looking into smart_ptr family of templates.
UPDATE
Thanks for the responses, I realize there's no escape from pointers. (see my other question, which is really a follow up on this one)
Upvotes: 2
Views: 1111
Reputation: 39818
C++ has something better than immutable types—const
. A single type can be mutable or not depending on your needs. That said, there are useful patterns for dealing with short-lifetime (near-)copies:
void f(const X &x) {
// Trivial case: unconditional copy
X x2=transform(x);
// Less trivial: conditional copy
std::optional<X> maybe;
const X &use=need_copy ? maybe.emplace(transform(x)) : x;
use.go(); // whichever, x or *maybe
} // *maybe destroyed iff created, then x2 destroyed
std::unique_ptr
may be used in a similar fashion prior to C++17, although the function can then of course throw std::bad_alloc
.
Upvotes: 0
Reputation: 29617
You need to make a new copy of your incoming argument. You can do what you want in several equivalent ways: 1) you could pass-by-value:
Imm Imm::someOp( Imm im ) const {
im = im.someOtherOp(); // local im is a copy, original im not modified
return im; // return by value (another copy)
}
or 2) you can pass-by-reference and make a copy explicitly:
Imm Imm::someOp( const Imm & im ) const {
Imm tmp = im.someOtherOp(); // local tmp is a copy
return tmp; // return by value (another copy)
}
both forms are equivalent.
Upvotes: 0
Reputation: 3013
Check this to know about temporary lifetime http://herbsutter.wordpress.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
Imm Imm::someOp( const Imm& p_im ) const
{
...
//Imm& im = p_im.someOtherOp(); // will *not* work
const Imm& im = p_im.someOtherOp(); // will work, but you get a const reference
...
}
But you can use boost::shared_ptr
shared_ptr<Imm> Imm::someOtherOp() const
{
shared_ptr<Imm> ret = new Imm;
...
return ret;
}
shared_ptr<Imm> Imm::someOp(const share_ptr<Imm>& p_im) const
{
shared_ptr<Imm> im = p_im->someOtherOp();
}
Upvotes: 0
Reputation: 67779
It doesn't work like that in C++.
When you pass a reference to an object, you are actually passing the address in memory of the object. References can't be re-seated to other objects either, hence the C++ adage "the reference IS the object." You HAVE to make a copy to modify it. Java will do this behind the scenes for you. C++, you just have to copy it.
Upvotes: 3
Reputation: 45493
In Java and C#, you are not really dealing with a reference - they are more like handles or pointers. A reference in C++ is really another name for the original object, not a pointer to it (although it may be implemented with a pointer). When you assign a value to a reference, you are assigning to the object itself. There is confusion in that to initialize a reference you can use the =
character, but it is an initialization, not an assignment.
Imm im, im2, im3;
Imm &imr = im; // initialize a reference to im
imr = im2; // assign im2 to imr (changes the value of im as well)
Imm *imp = &im; // initialize a pointer to the address of im
imp = &im3; // assign the address of im3 to imp (im is unnaffected);
(*imp) = im2; // assign im2 to imp (modifies im3 as well).
If you specifically want to pass "references by value" then you are essentially asking for a contradition in terms. References, by definition are passed by reference. As pointed out elsewhere, you can pass a pointer by value, or else a straight value. If you really want, you can hold onto a reference in a class and pass that around by value:
struct ImmRef
{
Imm &Ref;
ImmRef(Imm &ref) : Ref(ref) {}
};
Note also that a const applied to a reference is making the referred to object constant, not the reference. References are always const.
Upvotes: 4
Reputation: 11436
Didn't you forget to set the method you call as const?
EDIT: so, with the const fixed.
Maybe you should do something like
Imm & tmp = p_im.someOtherOp();
Then do further operation on the tmp variable.
If you set a variable or a parameter as const & you just cannot assign to it.
Upvotes: 1
Reputation: 256
Isn't assignment, by definition, not a constant operation?
You look like you're trying to assign something to a const reference, which totally defeats the idea of a const reference.
I think you may be looking for a pointer instead of a reference.
Upvotes: 4