In C++11, we can write this code:
struct Cat {
Cat(){}
};
const Cat cat;
std::move(cat); //this is valid in C++11
when I call std::move, it means I want to move the object, i.e. I will change the object. To move a const object is unreasonable, so why does std::move not restrict this behaviour? It will be a trap in the future, right?
Here trap means as Brandon mentioned in the comment:
" I think he means it "traps" him sneaky sneaky because if he doesn't
realize, he ends up with a copy which is not what he intended."
In the book 'Effective Modern C++' by Scott Meyers, he gives an example:
class Annotation {
public:
explicit Annotation(const std::string text)
: value(std::move(text)) //here we want to call string(string&&),
//but because text is const,
//the return type of std::move(text) is const std::string&&
//so we actually called string(const string&)
//it is a bug which is very hard to find out
private:
std::string value;
};
If std::move was forbidden from operating on a const object, we could easily find out the bug, right?
There's a trick here you're overlooking, namely that std::move(cat) doesn't actually move anything. It merely tells the compiler to try to move. However, since your class has no constructor that accepts a const CAT&&, it will instead use the implicit const CAT& copy constructor, and safely copy. No danger, no trap. If the copy constructor is disabled for any reason, you'll get a compiler error.
struct CAT
{
CAT(){}
CAT(const CAT&) {std::cout << "COPY";}
CAT(CAT&&) {std::cout << "MOVE";}
};
int main() {
const CAT cat;
CAT cat2 = std::move(cat);
}
prints COPY, not MOVE.
http://coliru.stacked-crooked.com/a/0dff72133dbf9d1f
Note that the bug in the code you mention is a performance issue, not a stability issue, so such a bug won't cause a crash, ever. It will just use a slower copy. Additionally, such a bug also occurs for non-const objects that don't have move constructors, so merely adding a const overload won't catch all of them. We could check for the ability to move construct or move assign from the parameter type, but that would interfere with generic template code that is supposed to fall back on the copy constructor.
And heck, maybe someone wants to be able to construct from const CAT&&, who am I to say he can't?
struct strange {
mutable size_t count = 0;
strange( strange const&& o ):count(o.count) { o.count = 0; }
};
const strange s;
strange s2 = std::move(s);
here we see a use of std::move on a T const. It returns a T const&&. We have a move constructor for strange that takes exactly this type.
And it is called.
Now, it is true that this strange type is more rare than the bugs your proposal would fix.
But, on the other hand, the existing std::move works better in generic code, where you don't know if the type you are working with is a T or a T const.
One reason the rest of the answers have overlooked so far is the ability for generic code to be resilient in the face of move. For example lets say that I wanted to write a generic function which moved all of the elements out of one kind of container to create another kind of container with the same values:
template <class C1, class C2>
C1
move_each(C2&& c2)
{
return C1(std::make_move_iterator(c2.begin()),
std::make_move_iterator(c2.end()));
}
Cool, now I can relatively efficiently create a vector<string> from a deque<string> and each individual string will be moved in the process.
But what if I want to move from a map?
int
main()
{
std::map<int, std::string> m{{1, "one"}, {2, "two"}, {3, "three"}};
auto v = move_each<std::vector<std::pair<int, std::string>>>(m);
for (auto const& p : v)
std::cout << "{" << p.first << ", " << p.second << "} ";
std::cout << '\n';
}
If std::move insisted on a non-const argument, the above instantiation of move_each would not compile because it is trying to move a const int (the key_type of the map). But this code doesn't care if it can't move the key_type. It wants to move the mapped_type (std::string) for performance reasons.
It is for this example, and countless other examples like it in generic coding that std::move is a request to move, not a demand to move.
I have the same concern as the OP.
std::move does not move an object, neither guarantees the object is movable. Then why is it called move?
I think being not movable can be one of following two scenarios:
1. The moving type is const.
The reason we have const keyword in the language is that we want the compiler to prevent any change to an object defined to be const. Given the example in Scott Meyers' book:
class Annotation {
public:
explicit Annotation(const std::string text)
: value(std::move(text)) // "move" text into value; this code
{ … } // doesn't do what it seems to!
…
private:
std::string value;
};
What does it literally mean? Move a const string to the value member - at least, that's my understanding before I reading the explanation.
If the language intends to not do move or not guarantee move is applicable when std::move() is called, then it is literally misleading when using word move.
If the language is encouraging people using std::move to have better efficiency, it has to prevent traps like this as early as possible, especially for this type of obvious literal contradiction.
I agree that people should be aware moving a constant is impossible, but this obligation should not imply the compiler can be silent when obvious contradiction happens.
2. The object has no move constructor
Personally, I think this is a separate story from OP's concern, as Chris Drew said
#hvd That seems like a bit of a non-argument to me. Just because OP's suggestion doesn't fix all bugs in the world doesn't necessarily mean it is a bad idea (it probably is, but not for the reason you give). – Chris Drew
I'm surprised nobody mentioned the backward compatibility aspect of this. I believe, std::move was purposely designed to do this in C++11. Imagine you're working with a legacy codebase, that heavily relies on C++98 libraries, so without the fallback on copy assignment, moving would break things.
Fortunately you can use clang-tidy's check to find such issues:
https://clang.llvm.org/extra/clang-tidy/checks/performance/move-const-arg.html
Related
In C++11, we can write this code:
struct Cat {
Cat(){}
};
const Cat cat;
std::move(cat); //this is valid in C++11
when I call std::move, it means I want to move the object, i.e. I will change the object. To move a const object is unreasonable, so why does std::move not restrict this behaviour? It will be a trap in the future, right?
Here trap means as Brandon mentioned in the comment:
" I think he means it "traps" him sneaky sneaky because if he doesn't
realize, he ends up with a copy which is not what he intended."
In the book 'Effective Modern C++' by Scott Meyers, he gives an example:
class Annotation {
public:
explicit Annotation(const std::string text)
: value(std::move(text)) //here we want to call string(string&&),
//but because text is const,
//the return type of std::move(text) is const std::string&&
//so we actually called string(const string&)
//it is a bug which is very hard to find out
private:
std::string value;
};
If std::move was forbidden from operating on a const object, we could easily find out the bug, right?
There's a trick here you're overlooking, namely that std::move(cat) doesn't actually move anything. It merely tells the compiler to try to move. However, since your class has no constructor that accepts a const CAT&&, it will instead use the implicit const CAT& copy constructor, and safely copy. No danger, no trap. If the copy constructor is disabled for any reason, you'll get a compiler error.
struct CAT
{
CAT(){}
CAT(const CAT&) {std::cout << "COPY";}
CAT(CAT&&) {std::cout << "MOVE";}
};
int main() {
const CAT cat;
CAT cat2 = std::move(cat);
}
prints COPY, not MOVE.
http://coliru.stacked-crooked.com/a/0dff72133dbf9d1f
Note that the bug in the code you mention is a performance issue, not a stability issue, so such a bug won't cause a crash, ever. It will just use a slower copy. Additionally, such a bug also occurs for non-const objects that don't have move constructors, so merely adding a const overload won't catch all of them. We could check for the ability to move construct or move assign from the parameter type, but that would interfere with generic template code that is supposed to fall back on the copy constructor.
And heck, maybe someone wants to be able to construct from const CAT&&, who am I to say he can't?
struct strange {
mutable size_t count = 0;
strange( strange const&& o ):count(o.count) { o.count = 0; }
};
const strange s;
strange s2 = std::move(s);
here we see a use of std::move on a T const. It returns a T const&&. We have a move constructor for strange that takes exactly this type.
And it is called.
Now, it is true that this strange type is more rare than the bugs your proposal would fix.
But, on the other hand, the existing std::move works better in generic code, where you don't know if the type you are working with is a T or a T const.
One reason the rest of the answers have overlooked so far is the ability for generic code to be resilient in the face of move. For example lets say that I wanted to write a generic function which moved all of the elements out of one kind of container to create another kind of container with the same values:
template <class C1, class C2>
C1
move_each(C2&& c2)
{
return C1(std::make_move_iterator(c2.begin()),
std::make_move_iterator(c2.end()));
}
Cool, now I can relatively efficiently create a vector<string> from a deque<string> and each individual string will be moved in the process.
But what if I want to move from a map?
int
main()
{
std::map<int, std::string> m{{1, "one"}, {2, "two"}, {3, "three"}};
auto v = move_each<std::vector<std::pair<int, std::string>>>(m);
for (auto const& p : v)
std::cout << "{" << p.first << ", " << p.second << "} ";
std::cout << '\n';
}
If std::move insisted on a non-const argument, the above instantiation of move_each would not compile because it is trying to move a const int (the key_type of the map). But this code doesn't care if it can't move the key_type. It wants to move the mapped_type (std::string) for performance reasons.
It is for this example, and countless other examples like it in generic coding that std::move is a request to move, not a demand to move.
I have the same concern as the OP.
std::move does not move an object, neither guarantees the object is movable. Then why is it called move?
I think being not movable can be one of following two scenarios:
1. The moving type is const.
The reason we have const keyword in the language is that we want the compiler to prevent any change to an object defined to be const. Given the example in Scott Meyers' book:
class Annotation {
public:
explicit Annotation(const std::string text)
: value(std::move(text)) // "move" text into value; this code
{ … } // doesn't do what it seems to!
…
private:
std::string value;
};
What does it literally mean? Move a const string to the value member - at least, that's my understanding before I reading the explanation.
If the language intends to not do move or not guarantee move is applicable when std::move() is called, then it is literally misleading when using word move.
If the language is encouraging people using std::move to have better efficiency, it has to prevent traps like this as early as possible, especially for this type of obvious literal contradiction.
I agree that people should be aware moving a constant is impossible, but this obligation should not imply the compiler can be silent when obvious contradiction happens.
2. The object has no move constructor
Personally, I think this is a separate story from OP's concern, as Chris Drew said
#hvd That seems like a bit of a non-argument to me. Just because OP's suggestion doesn't fix all bugs in the world doesn't necessarily mean it is a bad idea (it probably is, but not for the reason you give). – Chris Drew
I'm surprised nobody mentioned the backward compatibility aspect of this. I believe, std::move was purposely designed to do this in C++11. Imagine you're working with a legacy codebase, that heavily relies on C++98 libraries, so without the fallback on copy assignment, moving would break things.
Fortunately you can use clang-tidy's check to find such issues:
https://clang.llvm.org/extra/clang-tidy/checks/performance/move-const-arg.html
My default behaviour for any objects in local scopes is to make it const. E.g.:
auto const cake = bake_cake(arguments);
I try to have as little non-functional code as I can as this increases readability (and offers some optimisation opportunities for the compiler). So it is logical to also reflect this in the type system.
However, with move semantics, this creates the problem: what if my cake is hard or impossible to copy and I want to pass it out after I'm done with it? E.g.:
if (tastes_fine(cake)) {
return serve_dish(cake);
}
As I understand copy elision rules it's not guaranteed that the cake copy will be elided (but I'm not sure on this).
So, I'd have to move cake out:
return serve_dish(std::move(cake)); // this will not work as intended
But that std::move will do nothing useful, as it (correctly) will not cast Cake const& to Cake&&. Even though the lifetime of the object is very near its end. We cannot steal resources from something we promised not to change. But this will weaken const-correctness.
So, how can I have my cake and eat it too?
(i.e. how can I have const-correctness and also benefit from move semantics.)
I believe it's not possible to move from a const object, at least with a standard move constructor and non-mutable members. However, it is possible to have a const automatic local object and apply copy elision (namely NRVO) for it. In your case, you can rewrite your original function as follows:
Cake helper(arguments)
{
const auto cake = bake_cake(arguments);
... // original code with const cake
return cake; // NRVO
}
Then, in your original function, you can just call:
return serve_dish(helper(arguments));
Since the object returned by helper is already a non-const rvalue, it may be moved-from (which may be, again, elided, if applicable).
Here is a live-demo that demonstrates this approach. Note that there are no copy/move constructors called in the generated assembly.
You should indeed continue to make your variables const as that is good practice (called const-correctness) and it also helps when reasoning about code - even while creating it. A const object cannot be moved from - this is a good thing - if you move from an object you are almost always modifying it to a large degree or at least that is implied (since basically a move implies stealing the resources owned by the original object) !
From the core guidelines:
You can’t have a race condition on a constant. It is easier to reason
about a program when many of the objects cannot change their values.
Interfaces that promises “no change” of objects passed as arguments
greatly increase readability.
and in particular this guideline :
Con.4: Use const to define objects with values that do not change
after construction
Moving on to the next, main part of the question:
Is there a solution that does not exploit NRVO?
If by NRVO you take to include guaranteed copy elision, then not really, or yes and no at the same. This is somewhat complicated. Trying to move the return value out of a return by value function doesn't necessarily do what you think or want it to. Also, a "no copy" is always better than a move performance-wise. Therefore, instead you should try to let the compiler do it's magic and rely in particular on guaranteed copy elision (since you use c++17). If you have what I would call a complex scenario where elision is not possible: you can then use a move combined with guaranteed copy elision/NRVO, so as to avoid a full copy.
So the answer to that question is something like: if you object is already declared as const, then you can almost always rely on copy-elision/return by value directly, so use that. Otherwise you have some other scenario and then use discretion as to the best approach - in rare cases a move could be in order(meaning it's combined with copy-elision).
Example of 'complex' scenario:
std::string f() {
std::string res("res");
return res.insert(0, "more: ");//'complex scenario': a reference gets returned here will usually mean a copy is invoked here.
}
Superior way to 'fix' is to use copy-elision i.e.:
return res;//just return res as we already had that thus avoiding copy altogether - it's possible that we can't use this solution for more *hairy/complex* scenarios.
Inferior way to 'fix' in this example would be;
return std::move(res.insert(0, "more: "));
Make them movable if you can.
It's time to change your "default behaviour" as it's anachronistic.
If move semantics were built into the language from inception then making automatic variables const would have quickly become established as poor programming practice.
const was never intended to be used for micro-optimisations. Micro-optimisations are best left to the compiler. const exists primarily for member variables and member functions. It's also helped clean up the language a little: e.g. "foo" is a const char[4] type whereas in C it's a char[4] type with the curious understanding that you're not allowed to modify the contents.
Now (since C++11) const for automatic variables can actually be harmful as you observe, the time has come to stop this practice. The same can be said for const parameter by-value types. Your code would be less verbose too.
Personally I prefer immutable objects to const objects.
It seems to me, that if you want to move, than it will be "const correct" to not declare it const, because you will(!) change it.
It's ideological contradiction. You cannot move something and leave in place at the same time.
You mean, that object will be const for a part of time, in some scope. In this case, you can declare const reference to it, but it seems to me, that this will complicate the code and will add no safety.
Even vice versa, if you accidentally use the const reference to object after std::move() there will be problems, despite it will look like work with const object.
A limited workaround would be const move constructor:
class Cake
{
public:
Cake(/**/) : resource(acquire_resource()) {}
~Cake() { if (owning) release_resource(resource); }
Cake(const Cake& rhs) : resource(rhs.owning ? copy_resource(rhs.resource) : nullptr) {}
// Cake(Cake&& rhs) // not needed, but same as const version should be ok.
Cake(const Cake&& rhs) : resource(rhs.resource) { rhs.owning = false; }
Cake& operator=(const Cake& rhs) {
if (this == &rhs) return *this;
if (owning) release_resource(resource);
resource = rhs.owning ? copy_resource(rhs.resource) : nullptr;
owning = rhs.owning;
}
// Cake& operator=(Cake&& rhs) // not needed, but same as const version should be ok.
Cake& operator=(const Cake&& rhs) {
if (this == &rhs) return *this;
if (owning) release_resource(resource);
resource = rhs.resource;
owning = rhs.owning;
rhs.owning = false;
}
// ...
private:
Resource* resource = nullptr;
// ...
mutable bool owning = true;
};
Require extra mutable member.
not compatible with std containers which will do copy instead of move (providing non const version will leverage copy in non const usage)
usage after move should be considered (we should be in valid state, normally). Either provide owning getter, or "protect" appropriate methods with owning check.
I would personally just drop the const when move is used.
The following is an example of my typical code. A have a lot of objects that look like this:
struct Config
{
Config();
Config(const std::string& cType, const std::string& nType); //additional variables omitted
Config(Config&&) = default;
Config& operator=(Config&&) = default;
bool operator==(const Config& c) const;
bool operator!=(const Config& c) const;
void doSomething(const std::string& str);
bool doAnotherThing(const MyOtherObject& obj);
void doYetAnotherThing(int value1, unsigned long value2, const std::string& value3, MyEnums::Seasons value4, const std::vector<MySecondObject>& value5);
std::string m_controllerType;
std::string m_networkType;
//...
};
//...
Config::Config(const std::string& cType, const std::string& nType) :
m_controllerType(cType),
m_networkType(nType)
{
}
My motivations and general understand of the subject:
use const references in constructors and methods to avoid double-copying when passing objects.
simple types - pass by value; classes and structs - pass by const reference (or simple reference when I need to modify them)
force compiler to create default move constructor and move assignment so that It would be able to do it's fancy magic and simultaneously it allows to avoid writing boring ctor() : m_v1(std::move(v1)), m_v2(std::move(v2)), m_v3(std::move(v3)) {}.
if it performs badly, use libc and raw pointers, then wrap it at class and write a comment.
I have a strong feeling that by rules of thumb are flawed and simply incorrect.
After reading cppreference, Scott Mayers, C++ standard, Stroustrup and so on, I feel like: "Yea, I understand every word here, but it still doesn't make any sense'. The only thing I king of understood is that move semantics makes sense when my class contains non-copiable types, like std::mutex and std::unique_ptr.
I've seen a lot of code where people pass complex object by value, like large strings, vectors and custom classes - I believe this is where move semantics happen, but, again, how can you pass an object to a function by move? If I am correct, it would leave an object in a "kind-of-null-state", making it unusable.
So, the questionы are:
- How do I correctly decide between pass-by-value and pass-by-reference?
- Do I need to provide both copy and move constructors?
- Do I need to explicitly write move and copy constructors? May I use = default? My classes are mostly POD object so there is no complex login involved.
- When debugging, I can always write std::cout << "move\n"; or std::cout << "copy\n"; in constructors of my own classes, but how do I know what happens with classes from stdlib?
P.S. It may look like it is a cry out of desperation (it is), not a valid SO question. I simply don't know to formulate my problems better than this.
If it is a primitive type, pass by value. Locality of reference wins.
If you aren't going to store a copy of it, pass by value or const&.
If you want to store a copy of it, and it is very cheap to move and modestly expensive to copy, pass by value.
If something has a modest cost to move, and is a sink parameter, consider pass by rvalue reference. Users will be forced to std::move.
Consider providing a way for callers to emplace construct into the field in highly generic code, or where you need every ounce of performance
The Rule of 0/3/5 describes how you should handle copy assign/construct/destroy. Ideally you follow the rule of 0; copy/move/destruct is all =default in anything except resource management types. If you want to implement any of copy/move/destruct, you need to implement, =default or =delete every other one of the 5.
If you are only taking 1 argument to a setter, consider writing both the && and const& versions of the setter. Or just exposing the underlying object. Move-assignment sometimes reuses storage and that is efficient.
Emplacing looks like this:
struct emplace_tag {};
struct wrap_foo {
template<class...Ts>
wrap_foo(emplace_tag, Ts&&...ts):
foo( std::forward<Ts>(ts)... )
{}
template<class T0, class...Ts>
wrap_foo(emplace_tag, std::initializer_list<T0> il, Ts&&...ts):
foo( il, std::forward<Ts>(ts)... )
{}
private:
Foo foo;
};
there are a myriad of other ways you can permit "emplace" construction. See emplace_back or emplace in standard containers as well (where they use placement ::new to construct objects, forwarding objects passed in).
Emplace construct even permits direct construction without even a move using objects with an operator T() setup properly. But that is something that is beyond the scope of this question.
If a function return a value like this:
std::string foo() {
std::string ret {"Test"};
return ret;
}
The compiler is allowed to move ret, since it is not used anymore. This doesn't hold for cases like this:
void foo (std::string str) {
// do sth. with str
}
int main() {
std::string a {"Test"};
foo(a);
}
Although a is obviously not needed anymore since it is destroyed in the next step you have to do:
int main() {
std::string a {"Test"};
foo(std::move(a));
}
Why? In my opinion, this is unnecessarily complicated, since rvalues and move semantic are hard to understand especially for beginners. So it would be great if you wouldn't have to care in standard cases but benefit from move semantic anyway (like with return values and temporaries). It is also annoying to have to look at the class definition to discover if a class is move-enabled and benefits from std::move at all (or use std::move anyway in the hope that it will sometimes be helpfull. It is also error-prone if you work on existing code:
int main() {
std::string a {"Test"};
foo(std::move(a));
// [...] 100 lines of code
// new line:
foo(a); // Ups!
}
The compiler knows better if an object is no longer used used. std::move everywhere is also verbose and reduces readability.
It is not obvious that an object is not going to be used after a given point.
For instance, have a look at the following variant of your code:
struct Bar {
~Bar() { std::cout << str.size() << std::endl; }
std::string& str;
}
Bar make_bar(std::string& str) {
return Bar{ str };
}
void foo (std::string str) {
// do sth. with str
}
int main() {
std::string a {"Test"};
Bar b = make_bar(a);
foo(std::move(a));
}
This code would break, because the string a is put in an invalid state by the move operation, but Bar is holding a reference to it, and will try to use it when it's destroyed, which happens after the foo call.
If make_bar is defined in an external assembly (e.g. a DLL/so), the compiler has no way, when compiling Bar b = make_bar(a);, of telling if b is holding a reference to a or not. So, even if foo(a) is the last usage of a, that doesn't mean it's safe to use move semantics, because some other object might be holding a reference to a as a consequence of previous instructions.
Only you can know if you can use move semantics or not, by looking at the specifications of the functions you call.
On the other side, you can always use move semantics in the return case, because that object will go out of scope anyway, which means any object holding a reference to it will result in undefined behaviour regardless of the move semantics.
By the way, you don't even need move semantics there, because of copy elision.
Its all sums up on what you define by "Destroyed"? std::string has no special effect for self-destroying but deallocating the char array which hides inside.
what if my destructor DOES something special? for example - doing some important logging? then by simply "moving it because it's not needed anymore" I miss some special behavior that the destructor might do.
Because compilers cannot do optimizations that change behavior of the program except when allowed by the standard. return optimization is allowed in certain cases but this optimization is not allowed for method calls. By changing the behavior, it would skip calling copy constructor and destructor which can have side effects (they are not required to be pure) but by skipping them, these side effects won't happen and therefore the behavior would be changed.
(Note that this highly depends on what you try to pass and, in this case, STL implementation. In cases where all code is available at the time of compilation, the compiler may determine both copy constructor and destructor are pure and optimize them out.)
While the compiler is allowed to move ret in your first snippet, it might also do a copy/move elision and construct it directly into the stack of the caller.
This is why it is not recommended to write the function like this:
std::string foo() {
auto ret = std::string("Test");
return std::move(ret);
}
Now for the second snippet, your string a is a lvalue. Move semantics only apply to rvalue-references, which obtained by returning a temporary, unnamed object, or casting a lvalue. The latter is exactly what std::move does.
std::string GetString();
auto s = GetString();
// s is a lvalue, use std::move to cast it to rvalue-ref to force move semantics
foo(s);
// GetString returns a temporary object, which is a rvalue-ref and move semantics apply automatically
foo(GetString());
With the following code, "hello2" is not displayed as the temporary string created on Line 3 dies before Line 4 is executed. Using a #define as on Line 1 avoids this issue, but is there a way to avoid this issue without using #define? (C++11 code is okay)
#include <iostream>
#include <string>
class C
{
public:
C(const std::string& p_s) : s(p_s) {}
const std::string& s;
};
int main()
{
#define x1 C(std::string("hello1")) // Line 1
std::cout << x1.s << std::endl; // Line 2
const C& x2 = C(std::string("hello2")); // Line 3
std::cout << x2.s << std::endl; // Line 4
}
Clarification:
Note that I believe Boost uBLAS stores references, this is why I don't want to store a copy. If you suggest that I store by value, please explain why Boost uBLAS is wrong and storing by value will not affect performance.
Expression templates that do store by reference typically do so for performance, but with the caveat they only be used as temporaries
Taken from the documentation of Boost.Proto (which can be used to create expression templates):
Note An astute reader will notice that the object y defined above will be left holding a dangling reference to a temporary int. In the sorts of high-performance applications Proto addresses, it is typical to build and evaluate an expression tree before any temporary objects go out of scope, so this dangling reference situation often doesn't arise, but it is certainly something to be aware of. Proto provides utilities for deep-copying expression trees so they can be passed around as value types without concern for dangling references.
In your initial example this means that you should do:
std::cout << C(std::string("hello2")).s << std::endl;
That way the C temporary never outlives the std::string temporary. Alternatively you could make s a non reference member as others pointed out.
Since you mention C++11, in the future I expect expression trees to store by value, using move semantics to avoid expensive copying and wrappers like std::reference_wrapper to still give the option of storing by reference. This would play nicely with auto.
A possible C++11 version of your code:
class C
{
public:
explicit
C(std::string const& s_): s { s_ } {}
explicit
C(std::string&& s_): s { std::move(s_) } {}
std::string const&
get() const& // notice lvalue *this
{ return s; }
std::string
get() && // notice rvalue *this
{ return std::move(s); }
private:
std::string s; // not const to enable moving
};
This would mean that code like C("hello").get() would only allocate memory once, but still play nice with
std::string clvalue("hello");
auto c = C(clvalue);
std::cout << c.get() << '\n'; // no problem here
but is there a way to avoid this issue without using #define?
Yes.
Define your class as: (don't store the reference)
class C
{
public:
C(const std::string & p_s) : s(p_s) {}
const std::string s; //store the copy!
};
Store the copy!
Demo : http://www.ideone.com/GpSa2
The problem with your code is that std::string("hello2") creates a temporary, and it remains alive as long as you're in the constructor of C, and after that the temporary is destroyed but your object x2.s stills points to it (the dead object).
After your edit:
Storing by reference is dangerous and error prone sometimes. You should do it only when you are 100% sure that the variable reference will never go out of scope until its death.
C++ string is very optimized. Until you change a string value, all will refer to the same string only. To test it, you can overload operator new (size_t) and put a debug statement. For multiple copies of same string, you will see that the memory allocation will happen only once.
You class definition should not be storing by reference, but by value as,
class C {
const std::string s; // s is NOT a reference now
};
If this question is meant for general sense (not specific to string) then the best way is to use dynamic allocation.
class C {
MyClass *p;
C() : p (new MyClass()) {} // just an example, can be allocated from outside also
~C() { delete p; }
};
Without looking at BLAS, expression templates typically make heavy use of temporary objects of types you aren't supposed to even know exists. If Boost is storing references like this within theirs, then they would suffer the same problem you see here. But as long as those temporary objects remain temporary, and the user doesnt store them for later, everything is fine because the temporaries they reference remain alive for as long as the temporary objects do. The trick is you perform a deep copy when the intermediate object is turned into the final object that the user stores. You've skipped this last step here.
In short, it's a dangerous move, which is perfectly safe as long as the user of your library doesn't do anything foolish. I wouldn't recommend making use of it unless you have a clear need, and you're well aware of the consequences. And even then, there might be a better alternative, I've never worked with expression templates in any serious capacity.
As an aside, since you tagged this C++0x, auto x = a + b; seems like it would be one of those "foolish" things users of your code can do to make your optimization dangerous.