if i have T&& temp = std::move(other); then use this on a function that accepts T by value - c++11

so let's say i have a following function:
void foo(std::string strParam) // pass-by-value
{
// function-body
}
so strParam of foo(string) will either be created via copy (if arg was lvalue) or move (if arg was rvalue).
as everybody knows,
foo("blah"); // rvalue; so string move constructor invoked for strParam.
versus,
string bar = "blah";
foo(bar); // lvalue; so string copy constructor invoked for strParam.
again,
string bar = "blah";
foo(move(bar)); // xvalue; so move constructor.
and for named rvalue reference variable
string &&temp = // can be whatever
foo(temp); // *named* rvalue reference IS a lvalue; so copy constructor.
so i guess what that means is,
string &&movedBar = move(bar);
foo(movedBar); // it actually invokes copy constructor.
so invoking,
foo(move(bar))
is different from
string&& movedBar = move(bar);
foo(movedBar)
because one is unnamed rvalue reference (xvalue) and other is named rvalue reference (lvalue)
that's right, right?

One correction:
foo("blah"); // rvalue; so string move constructor invoked for strParam.
This actually invokes the std::string constructor that takes a const char* and not the std::string move constructor. That is the only std::string constructor in the overload set - everything else would involve more than one user-defined conversion.
On every other point, you are correct. To summarize:
foo("blah"); // calls string constructor that takes a const char*
foo(bar); // calls string copy ctor
foo(move(bar)); // calls string move ctor
string&& movedBar = move(bar);
foo(movedBar); // calls copy ctor
Update: As Tobias points out in the comments, foo("blah") will actually call two constructors, as if it were actually foo(string("blah")). First a temporary string is constructed from "blah" and that temporary is moved into strParam. However that second move will probably be elided, since string strParam(string("blah")) is redundant. This can be verified by delete-ing the move constructor of a custom widget or compiling with -fno-elide-constructors.
Or, as I like to look at it, we were both correct. The const char* is invoked and the string move constructor is invoked (~ish?).

Related

Immutable classes and copy constructors

I'm making a framework which will read a Tiled's XML file and the resulting object (tpp::File) will be pure immutable (no setters or copy constructors/assignment operators). Basically, it kind of uses the builder pattern idea, but instead of having 2 objects with the same attributes, I'll have one with the main attributes and another that will "wrap" it.
// Represents a Tiled's TMX file. This object is immutable.
class TILEDPP_API File final
{
public:
File() = default;
File(tpp::File&&) = default;
File(const tpp::File&) = delete;
File(const tpp::Path& path, tpp::FileMetadata& metadata);
File& operator = (tpp::File&&) = default;
File& operator = (const tpp::File&) = delete;
const tpp::Path& getPath() const;
const tpp::Header& getHeader() const;
const tpp::Layers& getLayers() const;
const tpp::TileSets& getTileSets() const;
private:
const tpp::Path m_path;
tpp::FileMetadata m_metadata; // Should be const!
};
// Represents the content of a Tiled's TMX file (header, sets, layers etc).
// This struct is non-copyable due to its HUGE size.
struct TILEDPP_API FileMetadata final
{
FileMetadata() = default;
FileMetadata(tpp::FileMetadata&&) = default;
FileMetadata(const tpp::FileMetadata&) = delete;
FileMetadata& operator = (FileMetadata&&) = default;
FileMetadata& operator = (const FileMetadata&) = delete;
tpp::Header header;
tpp::Layers layers;
tpp::TileSets sets;
};
Then, somewhere in the file creation process, we'll have this:
tpp::File FileReader::read(const std::string& path)
{
tpp::FileMetadata metadata = m_parser.parseMetadata(path);
return tpp::File(path, metadata);
}
The above snippet will use the File(const tpp::Path& path, tpp::FileMetadata& metadata) constructor, as expected. However, if we make tpp::File's tpp::FileMetadata const, it will try to use the File(const tpp::File&) constructor instead, which is deleted. Why does it even happen?!
For reference, the project can be found here. Any thoughts are also much appreciated.
it will try to use the File(const tpp::File&) constructor instead
Instead is the wrong word.
As well is the right word.
Prior to C++17, you are creating a temporary File and returning a move-constructed copy from the function. (This move construction may be elided, and in C++17 there is no move constructed copy any more).
FileMetaData does not support a move from FileMetadata const&&). So your =default in File(File&&) doesn't work; no default implementation can work.
Objects cannot be both immutable and practically movable-from.
Immutable objects cannot be practically moved-from in C++; while they are immutable during destruction, move-from happens before destruction, when they must be immutable. So you cannot "tear out their state" and move it somewhere else.
I state this, because you appear to want to support move-from:
File(tpp::File&&) = default;
File& operator = (tpp::File&&) = default;
If you want move-from, File cannot be immutable. If you want immutable, then File cannot be moved-from.
So, suppose you want to keep immutability.
Make those =delete, then change
return tpp::File(path, metadata)
to
return {std::move(path), std::move(metadata)};
and change
File(const tpp::Path& path, tpp::FileMetadata& metadata);
to
File(tpp::Path&& path, tpp::FileMetadata&& metadata);
and your code compiles.
To call read, store the return value in a File&& or a auto&& -- an rvalue reference to the temporary read returns.
Note you cannot store this returned File in a varible of longer lifetime, as that requires moving from the File, which immutable objects do not permit.
Alternatively, back off on immutability. Make it immutable except for moving.
Objects that are immutable except for moving do not store their data as const, because they mutate it when they move. Every other method on them is const.

C++ Do I always have to use std::move to invoke the move constructor?

"Theory" question if you will.
In order to execute/make use of the move constructor in a class, do I always have to use std::move(...) to tell the compiler that I wish to 'move' an object rather than copy it?
Are there any cases where the compiler will invoke the move constructor for me without the use of std::move? (My guess would be in function return values?)
According to cppreference.com (http://en.cppreference.com/w/cpp/language/move_constructor):
The move constructor is called whenever an object is initialized from xvalue of the same type, which includes
initialization, T a = std::move(b); or T a(std::move(b));, where b is of type T;
function argument passing: f(std::move(a));, where a is of type T and f is void f(T t);
function return: return a; inside a function such as T f(), where a is of type T which has a move constructor.
In most cases, yes std::move is needed.
The compiler will invoke the move constructor without std::move when:
returning a local variable by value
when constructing an object from an rvalue of the same type
In all other cases, use std::move. E.g.:
struct S {
std::string name;
S(std::string name) : name(std::move(name)) {}
};
and
std::unique_ptr<Base> func() {
auto p = std::make_unique<Derived>();
return std::move(p); // doesn't work without std::move
}
std::move is just a cast.
unique_ptr<int> global;
auto v = unique_ptr<int>(global); // global is a lvalue, therefore the
unique_ptr(unique_ptr<T>&v) constructor that accepts lvalue references is called.
auto v = unique_ptr<int>(std::move(global)); // move returns a &&rvalue reference, therefore the
unique_ptr(unique_ptr<T>&&v) constructor that accepts &&rvalue references is used.
When the criteria for elision of a copy operation are met and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
therefore,
unique_ptr<int> hello()
{
unique_ptr<int> local;
return local;
// local is an lvalue, but since the critera for elision is met,
// the returned object is created using local as if it was an rvalue
}
Also,
unique_ptr<int> hello = std::unique_ptr<int>();
// we have a pure rvalue in the right, therefore no std::move() cast is needed.

Assigning returned objects and copy constructor and destructor calls

So I am trying to experiment with some code to see if I understand copy constructor calls and destructor calls when passing values to functions. However, I'm confused:
#include <iostream>
class Test
{
public:
Test(int a)
{
std::cout<<"Constructor called"<<std::endl;
}
Test(const Test& copy)
{
std::cout<<"Copy constructor called"<<std::endl;
this->a = copy.a;
}
Test& operator=(const Test& copy)
{
this->a = copy.a;
std::cout<<"Copy assignment operator called"<<std::endl;
return *this;
}
//Test& operator=(Test&& move) = delete;
//Test(Test&& move) = delete;
/*Test& operator=(Test&& move)
{
this->a = move.a;
std::cout<<"Move assignment operator called"<<std::endl;
return *this;
}*/
/*Test(Test&& move)
{
this->a = move.a;
std::cout<<"Move constructor called"<<std::endl;
}*/
//invoked when passing 1...
int a;
~Test()
{
std::cout<<"Destructor called"<<std::endl;
}
};
Test function(Test a_test)
{
std::cout<<"In 'function'"<<std::endl;
std::cout<<"Returning"<<std::endl;
return a_test;//copy constructor called to assign to temporary value that is being returned, and then destructor for a_test is called
}
int main()
{
Test test1 = function(1);//copy constructor called again, and then destructor for temp value is called?
std::cout<<"DONE WITH THIS ROUND"<<std::endl<<std::endl;
test1 = function(1);//??
std::cout<<"DONE WITH THIS ROUND"<<std::endl<<std::endl;
return 0;
}
Can someone explain to me what the first line in main is doing?
To explain my confusion, from what I understand, main should call the function while passing the integer 1. Since Test 'a_test' is a parameter for the function, and it can take an integer for construction, it is constructed with the integer passed in as 1. Then we return 'a_test', in which case a copy constructor should be invoked to copy the contents of 'a_test' into the contents of the "return value". Then, the destructor should be called to delete the contents in 'a_test'. Then, the copy constructor should be invoked for 'test1', to copy the contents of the "return value". Then, after we are done copying the contents, the destructor should be called for the "return value".
However, I get the following output:
Constructor called
In 'function'
Returning
Copy constructor called
Destructor called
DONE WITH THIS ROUND
Constructor called
In 'function'
Returning
Copy constructor called
Copy assignment operator called
Destructor called
Destructor called
DONE WITH THIS ROUND
Destructor called
So it seems I have the wrong idea and feel totally confused.
I expected:
Constructor called
In 'function'
Returning
Copy constructor called
Destructor called
Copy constructor called
Destructor called
DONE WITH THIS ROUND
....
And then the rest of the output, that I think I could understand if I can understand the first line of main first.

Is my compiler optimizing away the rvalue returned from function? [duplicate]

What is copy elision? What is (named) return value optimization? What do they imply?
In what situations can they occur? What are limitations?
If you were referenced to this question, you're probably looking for the introduction.
For a technical overview, see the standard reference.
See common cases here.
Introduction
For a technical overview - skip to this answer.
For common cases where copy elision occurs - skip to this answer.
Copy elision is an optimization implemented by most compilers to prevent extra (potentially expensive) copies in certain situations. It makes returning by value or pass-by-value feasible in practice (restrictions apply).
It's the only form of optimization that elides (ha!) the as-if rule - copy elision can be applied even if copying/moving the object has side-effects.
The following example taken from Wikipedia:
struct C {
C() {}
C(const C&) { std::cout << "A copy was made.\n"; }
};
C f() {
return C();
}
int main() {
std::cout << "Hello World!\n";
C obj = f();
}
Depending on the compiler & settings, the following outputs are all valid:
Hello World!
A copy was made.
A copy was made.
Hello World!
A copy was made.
Hello World!
This also means fewer objects can be created, so you also can't rely on a specific number of destructors being called. You shouldn't have critical logic inside copy/move-constructors or destructors, as you can't rely on them being called.
If a call to a copy or move constructor is elided, that constructor must still exist and must be accessible. This ensures that copy elision does not allow copying objects which are not normally copyable, e.g. because they have a private or deleted copy/move constructor.
C++17: As of C++17, Copy Elision is guaranteed when an object is returned directly:
struct C {
C() {}
C(const C&) { std::cout << "A copy was made.\n"; }
};
C f() {
return C(); //Definitely performs copy elision
}
C g() {
C c;
return c; //Maybe performs copy elision
}
int main() {
std::cout << "Hello World!\n";
C obj = f(); //Copy constructor isn't called
}
Common forms of copy elision
For a technical overview - skip to this answer.
For a less technical view & introduction - skip to this answer.
(Named) Return value optimization is a common form of copy elision. It refers to the situation where an object returned by value from a method has its copy elided. The example set forth in the standard illustrates named return value optimization, since the object is named.
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
Regular return value optimization occurs when a temporary is returned:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
return Thing();
}
Thing t2 = f();
Other common places where copy elision takes place is when an object is constructed from a temporary:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
void foo(Thing t);
Thing t2 = Thing();
Thing t3 = Thing(Thing()); // two rounds of elision
foo(Thing()); // parameter constructed from temporary
or when an exception is thrown and caught by value:
struct Thing{
Thing();
Thing(const Thing&);
};
void foo() {
Thing c;
throw c;
}
int main() {
try {
foo();
}
catch(Thing c) {
}
}
Common limitations of copy elision are:
multiple return points
conditional initialization
Most commercial-grade compilers support copy elision & (N)RVO (depending on optimization settings). C++17 makes many of the above classes of copy elision mandatory.
Standard reference
For a less technical view & introduction - skip to this answer.
For common cases where copy elision occurs - skip to this answer.
Copy elision is defined in the standard in:
12.8 Copying and moving class objects [class.copy]
as
31) When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class
object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases,
the implementation treats the source and target of the omitted copy/move operation as simply two different
ways of referring to the same object, and the destruction of that object occurs at the later of the times
when the two objects would have been destroyed without the optimization.123 This elision of copy/move
operations, called copy elision, is permitted in the following circumstances (which may be combined to
eliminate multiple copies):
— in a return statement in a function with a class return type, when the expression is the name of a
non-volatile automatic object (other than a function or catch-clause parameter) with the same cvunqualified
type as the function return type, the copy/move operation can be omitted by constructing
the automatic object directly into the function’s return value
— in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a
function or catch-clause parameter) whose scope does not extend beyond the end of the innermost
enclosing try-block (if there is one), the copy/move operation from the operand to the exception
object (15.1) can be omitted by constructing the automatic object directly into the exception object
— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved
to a class object with the same cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the omitted copy/move
— when the exception-declaration of an exception handler (Clause 15) declares an object of the same type
(except for cv-qualification) as the exception object (15.1), the copy/move operation can be omitted
by treating the exception-declaration as an alias for the exception object if the meaning of the program
will be unchanged except for the execution of constructors and destructors for the object declared by
the exception-declaration.
123) Because only one object is destroyed instead of two, and one copy/move constructor is not executed, there is still one
object destroyed for each one constructed.
The example given is:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
and explained:
Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing:
the copying of the local automatic object t into the temporary object for the return value of function f()
and the copying of that temporary object into object t2. Effectively, the construction of the local object t
can be viewed as directly initializing the global object t2, and that object’s destruction will occur at program
exit. Adding a move constructor to Thing has the same effect, but it is the move construction from the
temporary object to t2 that is elided.
Copy elision is a compiler optimization technique that eliminates unnecessary copying/moving of objects.
In the following circumstances, a compiler is allowed to omit copy/move operations and hence not to call the associated constructor:
NRVO (Named Return Value Optimization): If a function returns a class type by value and the return statement's expression is the name of a non-volatile object with automatic storage duration (which isn't a function parameter), then the copy/move that would be performed by a non-optimising compiler can be omitted. If so, the returned value is constructed directly in the storage to which the function's return value would otherwise be moved or copied.
RVO (Return Value Optimization): If the function returns a nameless temporary object that would be moved or copied into the destination by a naive compiler, the copy or move can be omitted as per 1.
#include <iostream>
using namespace std;
class ABC
{
public:
const char *a;
ABC()
{ cout<<"Constructor"<<endl; }
ABC(const char *ptr)
{ cout<<"Constructor"<<endl; }
ABC(ABC &obj)
{ cout<<"copy constructor"<<endl;}
ABC(ABC&& obj)
{ cout<<"Move constructor"<<endl; }
~ABC()
{ cout<<"Destructor"<<endl; }
};
ABC fun123()
{ ABC obj; return obj; }
ABC xyz123()
{ return ABC(); }
int main()
{
ABC abc;
ABC obj1(fun123()); //NRVO
ABC obj2(xyz123()); //RVO, not NRVO
ABC xyz = "Stack Overflow";//RVO
return 0;
}
**Output without -fno-elide-constructors**
root#ajay-PC:/home/ajay/c++# ./a.out
Constructor
Constructor
Constructor
Constructor
Destructor
Destructor
Destructor
Destructor
**Output with -fno-elide-constructors**
root#ajay-PC:/home/ajay/c++# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors
root#ajay-PC:/home/ajay/c++# ./a.out
Constructor
Constructor
Move constructor
Destructor
Move constructor
Destructor
Constructor
Move constructor
Destructor
Move constructor
Destructor
Constructor
Move constructor
Destructor
Destructor
Destructor
Destructor
Destructor
Even when copy elision takes place and the copy-/move-constructor is not called, it must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
You should permit such copy elision only in places where it won’t affect the observable behavior of your software. Copy elision is the only form of optimization permitted to have (i.e. elide) observable side-effects. Example:
#include <iostream>
int n = 0;
class ABC
{ public:
ABC(int) {}
ABC(const ABC& a) { ++n; } // the copy constructor has a visible side effect
}; // it modifies an object with static storage duration
int main()
{
ABC c1(21); // direct-initialization, calls C::C(42)
ABC c2 = ABC(21); // copy-initialization, calls C::C( C(42) )
std::cout << n << std::endl; // prints 0 if the copy was elided, 1 otherwise
return 0;
}
Output without -fno-elide-constructors
root#ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp
root#ajay-PC:/home/ayadav# ./a.out
0
Output with -fno-elide-constructors
root#ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors
root#ajay-PC:/home/ayadav# ./a.out
1
GCC provides the -fno-elide-constructors option to disable copy elision.
If you want to avoid possible copy elision, use -fno-elide-constructors.
Now almost all compilers provide copy elision when optimisation is enabled (and if no other option is set to disable it).
Conclusion
With each copy elision, one construction and one matching destruction of the copy are omitted, thus saving CPU time, and one object is not created, thus saving space on the stack frame.
Here I give another example of copy elision that I apparently encountered today.
# include <iostream>
class Obj {
public:
int var1;
Obj(){
std::cout<<"In Obj()"<<"\n";
var1 =2;
};
Obj(const Obj & org){
std::cout<<"In Obj(const Obj & org)"<<"\n";
var1=org.var1+1;
};
};
int main(){
{
/*const*/ Obj Obj_instance1; //const doesn't change anything
Obj Obj_instance2;
std::cout<<"assignment:"<<"\n";
Obj_instance2=Obj(Obj(Obj(Obj(Obj_instance1)))) ;
// in fact expected: 6, but got 3, because of 'copy elision'
std::cout<<"Obj_instance2.var1:"<<Obj_instance2.var1<<"\n";
}
}
With the result:
In Obj()
In Obj()
assignment:
In Obj(const Obj & org)
Obj_instance2.var1:3

C++11 - Moving fundamental data types in constructor?

I'm looking into move semantics from C++11 and I'm curious how to move fundamental types like boolean, integer float etc. in the constructor. Also the compound types like std::string.
Take the following class for example:
class Test
{
public:
// Default.
Test()
: m_Name("default"), m_Tested(true), m_Times(1), m_Grade('B')
{
// Starting up...
}
Test(const Test& other)
: m_Name(other.m_Name), m_Times(other.m_Times)
, m_Grade(other.m_Grade), m_Tested(other.m_Tested)
{
// Duplicating...
}
Test(Test&& other)
: m_Name(std::move(other.m_Name)) // Is this correct?
{
// Moving...
m_Tested = other.m_Tested; // I want to move not copy.
m_Times = other.m_Times; // I want to move not copy.
m_Grade = other.m_Grade; // I want to move not copy.
}
~Test()
{
// Shutting down....
}
private:
std::string m_Name;
bool m_Tested;
int m_Times;
char m_Grade;
};
How do I move (not copy) m_Tested, m_Times, m_Grade. And is m_Name moved correctly? Thank you for your time.
Initialization and assignment of a primitive from a prvalue or xvalue primitive has exactly the same effect as initialization or assignment from a lvalue primitive; the value is copied and the source object is unaffected.
In other words, you can use std::move but it won't make any difference.
If you want to change the value of the source object (to 0, say) you'll have to do that yourself.
Looks correct. Except simple data types like bool, int, char are only copied. The point of "moving" a string is that it has a buffer that it normally has to copy when constructing a new object, however when moving the old buffer is used (copying the pointer and not the contents of the buffer).
Test(Test&& other)
: m_Name(std::move(other.m_Name)), m_Times(other.m_Times)
, m_Grade(other.m_Grade), m_Tested(other.m_Tested)
{}

Resources