I am looking to do something like this (C#).
public final class ImmutableClass {
public readonly int i;
public readonly OtherImmutableClass o;
public readonly ReadOnlyCollection<OtherImmutableClass> r;
public ImmutableClass(int i, OtherImmutableClass o,
ReadOnlyCollection<OtherImmutableClass> r) : i(i), o(o), r(r) {}
}
The potential solutions and their associated problems I've encountered are:
1. Using const for the class members, but this means the default copy assignment operator is deleted.
Solution 1:
struct OtherImmutableObject {
const int i1;
const int i2;
OtherImmutableObject(int i1, int i2) : i1(i1), i2(i2) {}
}
Problem 1:
OtherImmutableObject o1(1,2);
OtherImmutableObject o2(2,3);
o1 = o2; // error: use of deleted function 'OtherImmutableObject& OtherImmutableObject::operator=(const OtherImmutableObject&)`
EDIT: This is important as I would like to store immutable objects in a std::vector but receive error: use of deleted function 'OtherImmutableObject& OtherImmutableObject::operator=(OtherImmutableObject&&)
2. Using get methods and returning values, but this means that large objects would have to be copied which is an inefficiency I'd like to know how to avoid. This thread suggests the get solution, but it doesn't address how to handle passing non-primitive objects without copying the original object.
Solution 2:
class OtherImmutableObject {
int i1;
int i2;
public:
OtherImmutableObject(int i1, int i2) : i1(i1), i2(i2) {}
int GetI1() { return i1; }
int GetI2() { return i2; }
}
class ImmutableObject {
int i1;
OtherImmutableObject o;
std::vector<OtherImmutableObject> v;
public:
ImmutableObject(int i1, OtherImmutableObject o,
std::vector<OtherImmutableObject> v) : i1(i1), o(o), v(v) {}
int GetI1() { return i1; }
OtherImmutableObject GetO() { return o; } // Copies a value that should be immutable and therefore able to be safely used elsewhere.
std::vector<OtherImmutableObject> GetV() { return v; } // Copies the vector.
}
Problem 2: The unnecessary copies are inefficient.
3. Using get methods and returning const references or const pointers but this could leave hanging references or pointers. This thread talks about the dangers of references going out of scope from function returns.
Solution 3:
class OtherImmutableObject {
int i1;
int i2;
public:
OtherImmutableObject(int i1, int i2) : i1(i1), i2(i2) {}
int GetI1() { return i1; }
int GetI2() { return i2; }
}
class ImmutableObject {
int i1;
OtherImmutableObject o;
std::vector<OtherImmutableObject> v;
public:
ImmutableObject(int i1, OtherImmutableObject o,
std::vector<OtherImmutableObject> v) : i1(i1), o(o), v(v) {}
int GetI1() { return i1; }
const OtherImmutableObject& GetO() { return o; }
const std::vector<OtherImmutableObject>& GetV() { return v; }
}
Problem 3:
ImmutableObject immutable_object(1,o,v);
// elsewhere in code...
OtherImmutableObject& other_immutable_object = immutable_object.GetO();
// Somewhere else immutable_object goes out of scope, but not other_immutable_object
// ...and then...
other_immutable_object.GetI1();
// The previous line is undefined behaviour as immutable_object.o will have been deleted with immutable_object going out of scope
Undefined behaviour can occur due to returning a reference from any of the Get methods.
You truly want immutable objects of some type plus value semantics (as you care about runtime performance and want to avoid the heap). Just define a struct with all data members public.
struct Immutable {
const std::string str;
const int i;
};
You can instantiate and copy them, read data members, but that's about it. Move-constructing an instance from an rvalue reference of another one still copies.
Immutable obj1{"...", 42};
Immutable obj2 = obj1;
Immutable obj3 = std::move(obj1); // Copies, too
obj3 = obj2; // Error, cannot assign
This way, you really make sure every usage of your class respects the immutability (assuming no one does bad const_cast things). Additional functionality can be provided through free functions, there is no point in adding member functions to a read-only aggregation of data members.
You want 1., still with value semantics, but slightly relaxed (such that the objects aren't really immutable anymore) and you're also concerned that you need move-construction for the sake of runtime performance. There is no way around private data members and getter member functions:
class Immutable {
public:
Immutable(std::string str, int i) : str{std::move(str)}, i{i} {}
const std::string& getStr() const { return str; }
int getI() const { return i; }
private:
std::string str;
int i;
};
Usage is the same, but the move construction really does move.
Immutable obj1{"...", 42};
Immutable obj2 = obj1;
Immutable obj3 = std::move(obj1); // Ok, does move-construct members
Whether you want assignment to be allowed or not is under your control now. Just = delete the assignment operators if you don't want it, otherwise go with the compiler-generated one or implement your own.
obj3 = obj2; // Ok if not manually disabled
You don't care about value semantics and/or atomic reference count increments are ok in your scenario. Use the solution depicted in #NathanOliver's answer.
You can basically get what you want by leveraging a std::unique_ptr or std::shared_ptr. If you only want one of these objects, but allow for it to be moved around, then you can use a std::unique_ptr. If you want to allow for multiple objects ("copies") that all have the same value, then you can use a std::shared_Ptr. Use an alias to shorten the name and provide a factory function and it becomes pretty painless. That would make your code look like:
class ImmutableClassImpl {
public:
const int i;
const OtherImmutableClass o;
const ReadOnlyCollection<OtherImmutableClass> r;
public ImmutableClassImpl(int i, OtherImmutableClass o,
ReadOnlyCollection<OtherImmutableClass> r) : i(i), o(o), r(r) {}
}
using Immutable = std::unique_ptr<ImmutableClassImpl>;
template<typename... Args>
Immutable make_immutable(Args&&... args)
{
return std::make_unique<ImmutableClassImpl>(std::forward<Args>(args)...);
}
int main()
{
auto first = make_immutable(...);
// first points to a unique object now
// can be accessed like
std::cout << first->i;
auto second = make_immutable(...);
// now we have another object that is separate from first
// we can't do
// second = first;
// but we can transfer like
second = std::move(first);
// which leaves first in an empty state where you can give it a new object to point to
}
If the code is changes to use a shared_ptr instead then you could do
second = first;
and then both objects point to the same object, but neither can modify it.
Immutability in C++ can't be directly compared to immutability in most other popular languages because of C++'s universal value semantics. You have to figure out what you want "immutable" to mean.
You want to be able to assign new values to variables of type OtherImmutableObject. That makes sense, since you can do that with variables of type ImmutableObject in C#.
In that case, the simplest way to get the semantics you want is
struct OtherImmutableObject {
int i1;
int i2;
};
It may look like this is mutable. After all, you can write
OtherImmutableObject x{1, 2};
x.i1 = 3;
But the effect of that second line is (ignoring concurrency...) exactly the same as the effect of
x = OtherImmutableObject{3, x.i2};
so if you want to allow assignment to variables of type OtherImmutableObject then it makes no sense to disallow direct assignment to members, since it doesn't provide any additional semantic guarantee; all it does is make the code for the same abstract operation slower. (In this case, most optimizing compilers will probably generate the same code for both expressions, but if one of the members was a std::string they might not be smart enough to do that.)
Note that this is the behavior of basically every standard type in C++, including int, std::complex, std::string, etc. They are all mutable in the sense that you can assign new values to them, and all immutable in the sense that the only thing you can do (abstractly) to change them is assign new values to them, much like immutable reference types in C#.
If you don't want that semantics, your only other option is to forbid assignment. I would advise doing that by declaring your variables to be const, not by declaring all the members of the type to be const, because it gives you more options for how you can use the class. For example, you can create an initially mutable instance of the class, build a value in it, then "freeze" it by using only const references to it thereafter – like converting a StringBuilder to a string, but without the overhead of copying it.
(One possible reason to declare all members to be const might be that it allows for better optimization in some cases. For example, if a function gets an OtherImmutableObject const&, and the compiler can't see the call site, it isn't safe to cache the values of members across calls to other unknown code, since the underlying object may not have the const qualifier. But if the actual members are declared const, then I think it would be safe to cache the values.)
To answer your question, you don't create immutable data structures in C++ because consting references to the whole object does the trick. Violations of the rule are made visible by the presence of const_casts.
If I may refer to Kevlin Henney's "Thinking outside the synchronization quadrant", there are two questions to ask about data:
Is a structure immutable or mutable?
Is it shared or unshared?
These questions can be arranged into a nice 2x2 table with 4 quadrants. In a concurrent context, only one quadrant needs synchronization: shared mutable data.
Indeed, immutable data need not be synchronized because you cannot write to it, and concurrent reads are fine. Unshared data needs not be synchronized, because only the owner of the data can write to it or read from it.
So it is fine for a data structure to be mutable in an unshared context, and the benefits of immutability occur only in a shared context.
IMO, the solution that gives you most freedom is to define your class for both mutability and immutability, using constness only where it makes sense (data that is initalized then never changed):
/* const-correct */ class C {
int f1_;
int f2_;
const int f3_; // Semantic constness : initialized and never changed.
};
You can then use instances of your class C either as mutable or immutable, benefitting of constness-where-it-makes-sense in either case.
If now you want to share your object, you can pack it in a smart pointer to const:
shared_ptr<const C> ptr = make_shared<const C>(f1, f2, f3);
Using this strategy, your freedom spans the whole 3 unsynchronized quandrants while staying safely out of the synchronization quadrant. (therefore, limiting the need of making your structure immutable)
I'd say the most idiomatic way would be that:
struct OtherImmutable {
int i1;
int i2;
OtherImmutable(int i1, int i2) : i1(i1), i2(i2) {}
};
But... that not immutable??
Indeed but you can pass it around as a value:
void frob1() {
OtherImmutable oi;
oi = frob2(oi);
}
auto frob2(OtherImmutable oi) -> OtherImmutable {
// cannot affect frob1 oi, since it's a copy
}
Even better, places that don't need to mutate locally can define its local variables as const:
auto frob2(OtherImmutable const oi) -> OtherImmutable {
return OtherImmutable{oi.i1 + 1, oi.i2};
}
Immutable objects work much better with pointer semantics. So write a smart immutable pointer:
struct immu_tag_t {};
template<class T>
struct immu:std::shared_ptr<T const>
{
using base = std::shared_ptr<T const>;
immu():base( std::make_shared<T const>() ) {}
template<class A0, class...Args,
std::enable_if_t< !std::is_base_of< immu_tag_t, std::decay_t<A0> >{}, bool > = true,
std::enable_if_t< std::is_construtible< T const, A0&&, Args&&... >{}, bool > = true
>
immu(A0&& a0, Args&&...args):
base(
std::make_shared<T const>(
std::forward<A0>(a0), std::forward<Args>(args)...
)
)
{}
template<class A0, class...Args,
std::enable_if_t< std::is_construtible< T const, std::initializer_list<A0>, Args&&... >{}, bool > = true
>
immu(std::initializer_list<A0> a0, Args&&...args):
base(
std::make_shared<T const>(
a0, std::forward<Args>(args)...
)
)
{}
immu( immu_tag_t, std::shared_ptr<T const> ptr ):base(std::move(ptr)) {}
immu(immu&&)=default;
immu(immu const&)=default;
immu& operator=(immu&&)=default;
immu& operator=(immu const&)=default;
template<class F>
immu modify( F&& f ) const {
std::shared_ptr<T> ptr;
if (!*this) {
ptr = std::make_shared<T>();
} else {
ptr = std::make_shared<T>(**this);
}
std::forward<F>(f)(*ptr);
return {immu_tag_t{}, std::move(ptr)};
}
};
This leverages shared_ptr for most of its implementation; most of the disadvantages of shared_ptr are not a problem with immutable objects.
Unlike shared ptr, it permits you to create the object directly, and by default creates a non-null state. It can still reach a null state by being moved-from. You can create one in a null state by doing:
immu<int> immu_null_int{ immu_tag_t{}, {} };
and a non-null int via:
immu<int> immu_int;
or
immu<int> immu_int = 7;
I added a useful utility method called modify. Modify gives you a mutable instance of the T to pass to a lambda to modify before it is returned packaged up in an immu<T>.
Concrete use looks like:
struct data;
using immu_data = immu<data>;
struct data {
int i;
other_immutable_class o;
std::vector<other_immutable_class> r;
data( int i_in, other_immutable_class o_in, std::vector<other_immutable_class> r_in ):
i(i_in), o(std::move(o_in)), r( std::move(r_in))
{}
};
Then use immu_data.
Accessing members requires -> not ., and you should check for null immu_datas if you are passed them.
Here is how you use .modify:
immu_data a( 7, other_immutable_class{}, {} );
immu_data b = a.modify([&](auto& b){ ++b.i; b.r.emplace_back() });
This creates a b whose value is equal to a, except i is incremented by 1, and there is an extra other_immutable_class in b.r (default constructed). Note that a is unmodified by creating b.
There are probably typos above, but I've used the design.
If you want to get fancy, you can make immu support copy-on-write, or modify-in-place if unique. It is harder than it sounds though.
C++ doesn't quite have the ability to predefine a class as immutable or const.
And at some point you'll probably come to the conclusion that you shouldn't use const for class members in C++. It's just not worth the annoyances, and honestly you can do without it.
As a practical solution, I would try:
typedef class _some_SUPER_obtuse_CLASS_NAME_PLEASE_DONT_USE_THIS { } const Immutable;
to discourage anyone from using anything but Immutable in their code.
The issue at hand is a mistranslation from C# to C++. In C++ there is simply no* need to do this:
class ImmutableObject {
ImmutableObject(int i1, int i2) : i1(i1), i2(i2) {}
const int i1;
const int i2;
}
ImmutableObject o1(1,2):
ImmutableObject o2(2,3);
o1 = o2; // Doesn't compile, because immutable objects are by definition not mutable.
In your C# example you are using a class. And a variable that holds an instance of a class in C# is really just a reference to a garbage collected object. The closest equivalent in C++ is a reference counted smart pointer. So your c# example is translated to C++ as:
class ImmutableObject {
ImmutableObject(int i1, int i2) : i1(i1), i2(i2) {}
const int i1;
const int i2;
}
std::shared_ptr<ImmutableObject> o1 = std::make_shared<ImmutableObject>(1,2);
std::shared_ptr<ImmutableObject> o2 = std::make_shared<ImmutableObject>(2,3);
o1 = o2; // Does compile because shared_ptr is mutable.
There are several options if you want a mutable reference to an immutable/const object, specifically you can use a pointer, a smart pointer, or a reference_wrapper. Unless you actually want to have a class whose content can be changed by anyone at any time, which is the opposite of an immutable class.
*Of course, C++ is a language where "no" doesn't exist. In those precious few truly exceptional circumstances you can use const_cast.
I have a class that looks somewhat like this:
class S
{
public:
int* data;
S() : data(new int[10]) {}
};
The constructor allocates the memory of 10 integers, and the default copy constructor as expected merely copies the pointer itself rather than the content.
Even if there is an instance of S that has const modifier, I can modify the data that data points to, since that data itself does not have const modifier. I could avoid this by making data private and only allowing write access via a non-const method like so:
class S
{
private:
int* data;
public:
S() : data(new int[10]) {}
int& operator(size_t i)
{
return data[i];
}
const int& operator(size_t i) const
{
return data[i];
}
};
But now I can use the copy constructor to circumvent the constness of the instance of S like so:
void main()
{
const S a; // Allocates memory
S b(a); // Also points to memory allocated for a
b(1) = 3; // Writes to a even though it is not supposed to be mutable
}
What would be an elegant way to solve this problem (potentially using templates)?
The data pointed to by an instance of const S should not be mutable at all using only this instance.
Copy constructor should only copy pointer, but not make a deep copy of the data.
Both a const S and an S should be creatable via a copy constructor given an instance of S such that the const instance cannot modify the data, but the non-const instance can.
It is possible to know in the copy constructor if the object being copied is const by providing two different copy constructors, one which takes a const parameter and one which does not. The compiler will select whichever version matches the passed parameter. Set a flag in the constructor so it can throw an error when a non-const operation is performed.
The best way to avoid the leaked memory shown in the question is to used a smart pointer like std::shared_ptr rather than a raw pointer. Unfortunately shared_ptr is meant for single objects, not arrays; workarounds are possible as in this StackOverflow question. I'm not going to try to solve this now, the code below still has the leak.
To be complete you should follow the Rule of Three and provide an operator= and destructor as well. I left this as an exercise for the reader.
class S
{
private:
int* data;
bool is_const;
public:
S() : data(new int[10]), is_const(false) { data[1] = 42; }
S(const S& other) : data(other.data), is_const(true) {}
S(S& other) : data(other.data), is_const(false) {}
int& operator()(size_t i)
{
if (is_const)
throw std::logic_error("non-const operation attempted");
return data.ptr[i];
}
const int& operator()(size_t i) const
{
return data.ptr[i];
}
};
See it in action: http://ideone.com/SFN89M
Delete the copy constructor (and assignment operator) for S. Create a new proxy class (SCopy) that holds a pointer to an S object (which is passed in to the constructor for SCopy). SCopy would then implement the const int &operator() const and not the non-const version.
This would then allow you to implement a destructor in S that would free the memory you're currently leaking.
In the library I am designing, I sometimes need read-access to large member variables of classes. Because of their size I don't want to make a getter that returns by copying the member. I don't want them to be modifiable from the outside, so I can't have them public or return references to them. So I thought I would use a "reader":
class TestClass
{
public:
explicit TestClass(double d): d_(d){}
const double& readD() const { return d_; }
private:
double d_;
};
(this is not really meant for doubles)
But here somebody could const_cast the reference and access the data directly. Even without assuming malicious intent, somebody could safe a reference to the data-member and keep it around after the original object has gone out of scope. I know const references can keep a temporary viable, but that doesn't remove the const_cast-problem.
So I came up with a workaround:
#include <iostream>
template<class T>
class SafeMemberReference
{
public:
using type = T;
SafeMemberReference(const T& t) :t(t) {}
explicit SafeMemberReference(T&& t) = delete;
operator const T& () && {return t; }
T get() && {return t; }
private:
const T& t;
};
class TestClass
{
public:
explicit TestClass(double d): d_(d){}
SafeMemberReference<double> readD() const { return d_; }
private:
double d_;
};
int main()
{
TestClass foo(1.2);
// temporary from read can be used as temporary in expressions
std::cout << foo.readD() << std::endl;
// temporary can be used to copy from
auto x = foo.readD().get();
// lvalue can not be used, so a possible dangling reference is no problem
auto ref = foo.readD();
//std::cout << ref << std::endl;
}
I have several questions to this:
Q1) How necessary is this from an efficiency POV? the largest objects I am returning are dense complex matrices with dimensions of maybe 1000x1000. These copies may happen frequently
Q2) Are my concerns about returning by const& valid?
Q3) Does this seem like a good solution? which drawbacks does it have?
Any solution that attempts to fight the language itself is not a good solution.
They should get a wrap on the knuckles if they used a const_cast in that way: the behaviour on trying to change an object via a const_cast on an object that was originally declared as const is undefined. Even if you manage to conjure up a solution to prevent that, a hostile programmer could still take the address of your object, offset that address (using unsigned char* pointer arithmetic) and modify a data member through that pointer!
So if I were you I wouldn't fight the language. Return a const reference if I were you, as per your original suggestion.
Code static analysis tools / compiler warnings / code reviews / human resource departments will help you keep other collaborative programmers on the straight and narrow.
template <typename T>
void myswap(T a,T b)
{
T temp = a;
a = b;
b = temp;
}
int main()
{
int m(20),n(30);
myswap(ref(m),ref(n));
//m is still 20 and n is still 30
}
Why have not the values of m and n interchanged? Passing a value wrapped in std::ref to an INCREMENT function results in value change in the original variable (variable in the stack frame that calls INCREMENT function). Or, Is std::ref usage is restricted/limited?
std::ref (and its associated std::reference_wrapper) is a tool designed for very specific use cases in the standard library and should only be used for those; if you want to use it in your own places, you have to understand it in detail and respect what it does.
Fundamentally, a reference_wrapper is much closer to a pointer than a reference. So substitute a pointer in your swap function and you'll see that there's no reason to assume that it would actually swap:
void myswap(int* a, int* b)
{
int* temp = a;
a = b;
b = temp;
}
Your code creates two temporary std::reference_wrapper objects and swaps them, so they refer to different objects. All that happens is you swap two reference_wrapper objects, not their targets.
If you manually write what the function template will generate the reason for the behaviour should be obvious:
void myswap(std::reference_wrapper<int> a, std::reference_wrapper<int> b)
{
std::reference_wrapper<int> temp = a;
a = b;
b = a;
}
Obviously this doesn't change the int objects, only the reference_wrapper objects.
If what you're trying to do is force myswap to take references you need to call myswap<int&>(m, n), you can't emulate that by using reference_wrapper. But you should really fix myswap, because it's pretty useless the way it's written now.
Your myswap takes the elements by value.
Essentially you swap the two references (std::reference_wrapper s) at the local scope of the function.
The values they point to won't change.
template <typename T> void incrementer(T a) { ++a; } int a = 20;
In this case there's a conversion to int& with:
operator T& () const noexcept { return *_ptr; }
In your code, on the other hand:
T temp = a;
will simply call the copy constructor, which copies the underlying pointer:
reference_wrapper(const reference_wrapper&) noexcept = default;
then, on the next lines you again copy the pointer:
reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
I'm having some trouble trying to understand proper usage of returned references in C++.
I have a class with a "big" object inside. I want to be able to use this object outside the class "read only" mode by returning a const reference to it.
The problem is I don't quite understand when objects get copied and when they don't.
Most of the questions about returning references where about returning objects allocating on the stack, which is not my particular problem, so I've prepared this little example:
class foo {
int a;
public:
foo() {
a = 3;
}
int& getInt() {
return a;
}
const int& useInt() {
return a;
}
void print() {
cout << "Inside class: a = " << a << endl;
}
};
int main() {
foo foo1;
int& goodRef = foo1.getInt();
int badRef = foo1.getInt();
goodRef = 4;
badRef = 5;
foo1.print();
foo1.getInt() = 6;
foo1.print();
int usingIt = 10*foo1.useInt();
}
What I understand is:
In int& goodRef = foo1.getInt(); nothing is copied, only the class owned a exists.
In int badRef = foo1.getInt(); badRef is a copy of a, so there are 2 separate objects, a and badRef.
So depending on what type of object catches the return value it is copied or it is not, so my question is:
When I use the reference like in int usingIt = 10*foo1.useInt(); is it copied and then used to multiply or only the value inside the class is used?
In this example it doesn't matter since it is only an int, but if it was a big object it would be a big deal.
Thanks!
Edit: Thanks to all the answers, but I get that having such methods inside a class is bad, I only put them for the sake of the example.
My actual class has a bunch of objects inside and an interface and bla bla, but one particular object is a glm::mat4. What i want is to be able to write something like glm::vec4 finalVec = foo1.getMatrix() * vec. But I dont want the whole matrix to be copied and then multiplied, rather I want to use the matrix that is already inside my class to perform the multiplication and at the same time not let the user modify it. I supposed something similar to useInt but with the mat4 would work but I wasn't sure and that's why I asked the question.
The glm specificatioon is very confuse for me, but I think the operator * is described as:
glm::mat4 operator* (glm::mat4 const & m, glm::vec4 const & v);
In your example int usingIt = 10*foo1.useInt();, the operator*(), depending on it's argument signature and internals, could cause a copy of your object to take place, at which point it would then be assigned (rather than copied again) into the value of usingIt. If your object was an aggregate or class object type, the copy using the object type's assignment operator would typically be elided using a compiler optimization step.
In general, anytime you are copying a l-value reference type (i.e., T& or const T&) to an aggregate or class object to a non-reference type (i.e., type T), a copy constructor, constructor, assignment operator, or conversion operator is invoked on the returned reference to the object.
If getInt returns int& why are you catching the return value in an int? What do you expect, that the compiler changes your source code to use a reference to int? It can't do anything else than copying the value of the object pointed by the reference.
On the other hand I think you have a bad example. Either you define a single method that returns a const reference:
int const & getInt() { return a; }
Or you provide two methods, one const and one non const:
int& getInt() { return a; }
int const & getInt() const { return a; }
Having both getInt and useInt does not stop anyone from using getInt and actually changing the value of the object in an way that is not intended.
When you use const reference you can only use const methods of this object. E.g. when you have STL vector you can get size, but you can't push_back elements to it.
vector<int> a;
vector<int> &c = a;
const vector<int> &b = a;
a.size();
b.size();
a.push_back(4);
c.push_back(4);
//our vector is now 4 4
//b.push_back(4); compilation error
In C++ you copy when you want to copy: for example
Function which copies:
int x(vector<int> b)
Function which doesn't copy:
int x(vector<int> &b)
You can read more here