This is related to the (currently) closed question I asked earlier: Can you mutate an object of custom type when it's declared as constant?
Suppose we have something that looks like the following:
class test
{
public:
test() : i{4}, ptr{&i} {};
int i;
int *ptr;
int *get_ptr() const {return ptr;}
};
void func(const test &t, int j) {
*(t.get_ptr()) = j;
// auto ptr = t.get_ptr();
// *ptr = j;
}
int main(int argc, char const *argv[])
{
test t;
std::cout << t.i << std::endl;
func(t, 5);
std::cout << t.i << std::endl;
}
We have this func that takes in a const test &. When I see this signature (and if I didn't look at the implementation of the function), it makes me want to assume that nothing in t will get modified; however, the member variable i is able to be modified through the ptr member variable, as we see here.
I don't usually write code that end up working this way, so I'm wondering if code like this is discouraged?
Furthermore, is it reasonable to assume (most of the time) that an object declared as const will not be mutated?
Yes code like this is definitely discouraged. It completely ignores the reason we have the const keyword in the first place. The function is deliberately modifying something that it is advertising it will not modify
That means that whoever wrote the get_ptr() function messed up because they declared it const but let it return a pointer to non-const object (so one that can be changed, defeating the purpose of declaring the function const)
If whatever get_ptr() returns could properly be modified by such a function then it should be an implementation detail, a private (or protected) variable marked with the mutable keyword.
test::get_ptr() should have two overloads.
const int* get_ptr() const { return ptr; }
int* get_ptr() { return ptr; }
If func() wants to change the test object given to it then it should take test&, not const test&
The key is here:
int *get_ptr() const {return ptr;}
You define get_ptr as const which is akin to saying "get_ptr is not going to change any attributes of the class". And it doesn't. It returns the value of the attribute ptr, which is a int*, and points to a mutable instance of an int which happens to be an attribute of the class as well.
The compiler has no way of knowing this, so from the compiler's perspective the promise was kept. However in reality you're circumventing the const qualifier and allowing to mutate an otherwise immutable attribute.
Not the best of coding practices, but whoever writes such code should, obviously, not expect for i to remain as it was set in the class methods if get_ptr is ever called.
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.
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.
Is there a way to tell if an instance has been constructed in temporary scope or not, or prevent it from being used outside of temporary scope? I'm guessing there's not, but then again, I'm always surprised by the ability of C++ to exceed its own design limitations.
It's kind of a weird question, I admit, and I don't know how to "justify" the desire short of just providing the backstory.
The question arises from a shuttle class we use to glue together a scary number of legacy systems, each with their own notion of how data is represented. For a familiar example, take strings. We could overload each method in our API with each "style" of string:
void some_method(const char* arg);
void some_method(const std::string& arg);
void some_method(const QString& arg);
void some_method(const XmlDocString& arg);
void some_method(const wire_string& arg);
Or we could do:
void some_method(const StringArg& arg);
Where that helper class is (let's ignore string encodings for now and just assume bad old C-style strings for the purposes of this question):
class StringArg {
public:
StringArg() : m_data(""), m_len(0) {}
template<size_t N>
StringArg(const char (&s)[N]) : m_data(s), m_len(N-1) {}
StringArg(const char* s) : m_data(s?s:"") { m_len = strlen(m_data); }
template<class T>
StringArg(const T& t) : m_data(data_from(t)), m_len(len_from(t)) {}
const char* data() const { return m_data; }
const char* size() const { return m_len; }
private:
const char* m_data;
size_t m_len;
};
const char* data_from(const std::string& s) { return s.c_str(); }
size_t len_from(const std::string& s) { return s.size(); }
template<class XmlType>
const char* data_from(const XmlString<XmlType>& s) { return &s.content()[0]; }
template<class XmlType>
size_t len_from(const XmlString<XmlType>& s) { return s.byte_length(); }
ADL chooses the various data_from()/len_from() to get us a buffer backed by something else and its size. In reality there's extra metadata to capture important information about the nature of the buffer and how to iterate it, but the important point for this discussion is that StringArg is used in temporary scope, is cheap to copy, provides fast access to some buffer backed by something else on the outside of the interface whose type we now don't actually need to care about, and that any conversion, argument checking, or length calculations are done once at the boundary.
So there we are, someone is free to call it with two wildly different string classes:
interface_method(header() + body.str() + tail(), document.read().toutf8());
We don't need to care about the lifetime or the type of whatever's going on here, and internally we can pass around pointers to those buffers like candy, slice them up, parse them, log them in triplicate, without accidental allocation or lengthy memory copies. As long as we never hang on to those buffers, internally, this is safe and fast and has been a joy to maintain.
But as this API becomes more widely used, StringArg is (perhaps unsurpisingly) being used in places other than temporary scope, as if it were Yet Another String Class, and the resulting fireworks are impressive. Consider:
std::string t("hi");
write(StringArg(t+t)); //Yes.
StringArg doa(t+t); //NO!
write(doa); //Kaboom?
t+t creates a temporary whose content StringArg will point into. In temporary scope this is routine, nothing interesting to see here. Outside of it, of course, it's insanely dangerous. Dangling pointers to random stack memory. Of course, the second call to write() actually will work just fine most of the time, even though it is most clearly wrong, which makes detecting these mistakes quite difficult.
And here we are. I want to allow:
void foo(const StringArg& a);
foo(not_string_arg());
foo(t+t);
I want to prevent or detect:
StringArg a(t+t); //No good
And I'd be fine if the following wasn't possible, too, even though it's fine:
foo(StringArg(t+t)); //Meh
If I could detect the scope this thing was being constructed in, I could actually go and arrange to copy the content into a stable buffer in the constructor, similar to std::string, or throw an exception at runtime, or even better, if I could prevent it at compile time that'd ensure it was only used as designed.
Really, though, I only want StringArg to ever be the type of a method argument. An end user will never have to type "StringArg" in order to use the API. Ever. You'd hope that'd be easy enough to document away, but once some code looks like it works, it multiplies, and multiplies...
I have tried making StringArg non-copyable but that doesn't help much. I have tried creating an additional shuttle class and a non-const reference to try and fake out the implicit conversions in such a way they go my way. The explicit keyword seems to make my problem worse, promoting the typing of "StringArg". I tried messing around with an additional struct with partial specialization which is the only thing that knows how to construct a StringArg, and hiding the constructors for StringArg... something like:
template<typename T> struct MakeStringArg {};
template<> struct MakeStringArg<std::string> {
MakeStringArg(const std::string& s);
operator StringArg() const;
}
So then the user has to wrap all arguments with MakeStringArg(t+t) and MakeFooArg(foo) and MakeBarArg(bar)... existing code doesn't compile and in any case it kind of kills joy of using the interface.
I'm not above macro hacks at this point. My bag of tricks is looking pretty empty about now. Anyone have any advice?
So Matt McNabb points out
std::string t("hi");
const StringArg& a = t + t;
This causes a temporary StringArg to live longer than the content it points to. What I actually need is a way to determine when the full expression in which StringArg was constructed has ended. And that's actually doable:
class StringArg {
public:
template<class T>
StringArg(const T& t, const Dummy& dummy = Dummy())
: m_t(content_from(t)), m_d(&dummy) {
m_d->attach(this);
}
~StringArg() { if (m_d) m_d->detach(); }
private:
void stale() {
m_t = ""; //Invalidate content
m_d = NULL; //Don't access dummy anymore
//Optionally assert here
}
class Dummy {
public:
Dummy() : inst(NULL) {}
~Dummy() { if (inst) inst->stale(); }
void attach(StringArg* p) { inst = p; }
void detach() { inst = NULL; }
StringArg* inst;
};
friend class Dummy;
private:
const char* m_t;
Dummy* m_d;
};
With this, Matt's example and all the others I was hoping to prevent are thwarted: when the full expression ends, no StringArg points to anything suspect any longer, so any StringArg "given a name" is guaranteed to be useless.
(In case it's not clear why this works, it's because a Dummy must have been constructed before a StringArg that uses it, and therefore StringArg is guaranteed to be destroyed before the Dummy unless its lifetime is greater than the full expression in which it was constructed.)
I admit I didn't read your entire post, but you seem to have conflicting requirements. On the one hand you state that you want to avoid dangling references, but then you write:
write(StringArg(t+t)); //Yes.
StringArg doa(t+t); //NO!
If your only concern is to avoid dangling references then change the "NO!" to a "Yes", and in both cases move out of the temporary into a local value. The constructor would be:
StringArg(std::string &&arg)
{
this->the_arg = std::move(arg);
}
where the_arg is a std::string.
You could have StringArg store the string when it was constructed from an rvalue, and hold a reference to a string if it was constructed from an lvalue.
If you want a class which methods could be used only by rvalue objects, you could use rvalue qualifier on methods in C++11:
class only_rvalue
{
public:
only_rvalue() = default;
only_rvalue( const only_rvalue& ) = delete;
only_rvalue( only_rvalue&& ) = default;
only_rvalue& operator=( const only_rvalue&& ) = delete;
only_rvalue& operator=( only_rvalue&& ) = default;
void foo() &&;
void bar() &&;
void quux() &&;
};
only_rvalue create();
int main()
{
only_rvalue{}.foo(); //ok
create().bar(); //ok
only_rvalue lvalue;
lvalue.foo(); //ERROR
}
If there is something like below in code:
func(const base& obj)
what does the const semantics mean? What is constant here ? Is obj a const reference to a non-const object or a non-const reference to a const object?
There is no such thing as a "non-const" reference, that is, a reference is always bound to the same object and there is no way to change that. "const type&" means reference to const type.
obj is a reference to a const object.
There is no such thing as a "non-const reference", since a reference cannot be changed to refer to something else after it has been created.
It's called a const reference. You have a ' referential access ' to data that's passed but you cannot modify it.
Without const you will be not able to send a const object to that function. So it is always a positive to add const. especially when you are creating function for many users. Classic examples are setters functions.
x->setXsth(sth& obj) // works only with non-const object.
x->setXsth(const sth& obj) //works with const object and non-const.
obj is a reference to const base, so it means you are not allowed to change referenced object. It can be written as
func(const base& obj)
or
func(base const & obj)
Use right-left rule for reading such declarations types, for this simple example just read it from the right. More on that here:
http://www.codeproject.com/KB/cpp/complex_declarations.aspx
obj is a constant reference to an object (wether the object is const or non const) passed in argument to func()
if you write : func(B);
this means that you cannot change the content of B inside the function func()
(where func(const base& obj))
Somewhat unsolicited answer/viewpoint: The const modifier modifies whatever is on its left-hand-side, except for the one construction that you're using (in which case it modifies whatever is immediately to the right). I find it easier to always stick const immediately on the right of whatever I want to modify, and read the statement right-to-left. Maybe this isn't the best way to do things, but it helps me to keep it straight.
Examples:
// these two statements are equivalent
const int x = 5; // special case usage
int const x = 5;
// using the LHS syntax makes multiple consts easier to understand
int const y = 6;
int const * const x = &y; // x is a const pointer to const int
// const can apply to pointers but not to references
int const & const z = y; // redundant, references are always const
As other answers have said, obj is a reference to a const base object. However, that doesn't mean the object it refers to either has exactly the base type, or that the object it refers to is const, just that func can't modify obj through that reference. For example:
struct derived : base { ... };
derived d;
func(d);
is legal, and:
bool other_func(const base& b, other_object& o) {
base b_copy = b;
o.foo();
return b_copy == b;
}
may return false if o has an internal non-const reference to b (or something inside it) and o.foo() modifies b. This has practical implications for functions like
std::string::operator=(const std::string& other);
where a naive implementation might do the wrong thing for my_str = my_str.