M.4 - STD - Move - Learn C++

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 18

05/05/2021 M.

4 — std::move | Learn C++

M.4 — std::move
BY ALEX ON MARCH 4TH, 2017 | LAST MODIFIED BY ALEX ON APRIL 26TH, 2021

Once you start using move semantics more regularly, you’ll start to find cases where you want to invoke move
semantics, but the objects you have to work with are l-values, not r-values. Consider the following swap function as an
example:

1 #include <iostream>
2 #include <string>
3
4 template<class T>
5 void myswap(T& a, T& b)
6 {
7 T tmp { a }; // invokes copy constructor
8 a = b; // invokes copy assignment
9 b = tmp; // invokes copy assignment
10 }
11
12 int main()
13 {
14 std::string x{ "abc" };
15 std::string y{ "de" };
16
17 std::cout << "x: " << x << '\n';
18 std::cout << "y: " << y << '\n';
19
20 myswap(x, y);
21
22 std::cout << "x: " << x << '\n';
23 std::cout << "y: " << y << '\n';
24
25 return 0;
26 }

Passed in two objects of type T (in this case, std::string), this function swaps their values by making three copies.
Consequently, this program prints:

x: abc
y: de
x: de
y: abc

As we showed last lesson, making copies can be inefficient. And this version of swap makes 3 copies. That leads to a
lot of excessive string creation and destruction, which is slow.

However, doing copies isn’t necessary here. All we’re really trying to do is swap the values of a and b, which can be
accomplished just as well using 3 moves instead! So if we switch from copy semantics to move semantics, we can
make our code more performant.

But how? The problem here is that parameters a and b are l-value references, not r-value references, so we don’t
have a way to invoke the move constructor and move assignment operator instead of copy constructor and copy
assignment. By default, we get the copy constructor and copy assignment behaviors. What are we to do?

std::move

In C++11, std::move is a standard library function that casts (using static_cast) its argument into an r-value reference,
so that move semantics can be invoked. Thus, we can use std::move to cast an l-value into a type that will prefer
being moved over being copied. std::move is defined in the utility header.

Here’s the same program as above, but with a myswap() function that uses std::move to convert our l-values into r-
values so we can invoke move semantics:

https://www.learncpp.com/cpp-tutorial/stdmove/ 1/18
05/05/2021 M.4 — std::move | Learn C++
1 #include <iostream>
2
#include <string>
3
#include <utility> // for std::move
4
5
template<class T>
6
void myswap(T& a, T& b)
7
{
8
T tmp { std::move(a) }; // invokes move constructor
9
a = std::move(b); // invokes move assignment
10
b = std::move(tmp); // invokes move assignment
11
}
12
13
int main()
14
{
15
std::string x{ "abc" };
16
std::string y{ "de" };
17
18
std::cout << "x: " << x << '\n';
19
std::cout << "y: " << y << '\n';
20
21
myswap(x, y);
22
23
std::cout << "x: " << x << '\n';
24
std::cout << "y: " << y << '\n';
25
26
return 0;
27
}

This prints the same result as above:

x: abc
y: de
x: de
y: abc

But it’s much more efficient about it. When tmp is initialized, instead of making a copy of x, we use std::move to
convert l-value variable x into an r-value. Since the parameter is an r-value, move semantics are invoked, and x is
moved into tmp.

With a couple of more swaps, the value of variable x has been moved to y, and the value of y has been moved to x.

Another example

We can also use std::move when filling elements of a container, such as std::vector, with l-values.

In the following program, we first add an element to a vector using copy semantics. Then we add an element to the
vector using move semantics.

1 #include <iostream>
2 #include <string>
3 #include <utility> // for std::move
4 #include <vector>
5
6 int main()
7 {
8 std::vector<std::string> v;
9 std::string str = "Knock";
10
11 std::cout << "Copying str\n";
12 v.push_back(str); // calls l-value version of push_back, which copies str into the arr
13 ay element
14
15 std::cout << "str: " << str << '\n';
16 std::cout << "vector: " << v[0] << '\n';
17
18 std::cout << "\nMoving str\n";
19

https://www.learncpp.com/cpp-tutorial/stdmove/ 2/18
05/05/2021 M.4 — std::move | Learn C++
20 v.push_back(std::move(str)); // calls r-value version of push_back, which moves str in
21 to the array element
22
23 std::cout << "str: " << str << '\n';
24 std::cout << "vector:" << v[0] << ' ' << v[1] << '\n';
25
return 0;
}

This program prints:

Copying str
str: Knock
vector: Knock

Moving str
str:
vector: Knock Knock

In the first case, we passed push_back() an l-value, so it used copy semantics to add an element to the vector. For
this reason, the value in str is left alone.

In the second case, we passed push_back() an r-value (actually an l-value converted via std::move), so it used move
semantics to add an element to the vector. This is more efficient, as the vector element can steal the string’s value
rather than having to copy it. In this case, str is left empty.

At this point, it’s worth reiterating that std::move() gives a hint to the compiler that the programmer doesn’t need this
object any more (at least, not in its current state). Consequently, you should not use std::move() on any persistent
object you don’t want to modify, and you should not expect the state of any objects that have had std::move() applied
to be the same after they are moved!

Move functions should always leave your objects in a well-defined state

As we noted in the previous lesson, it’s a good idea to always leave the objects being stolen from in some well-defined
(deterministic) state. Ideally, this should be a “null state”, where the object is set back to its uninitiatized or zero state.
Now we can talk about why: with std::move, the object being stolen from may not be a temporary after all. The user
may want to reuse this (now empty) object again, or test it in some way, and can plan accordingly.

In the above example, string str is set to the empty string after being moved (which is what std::string always does
after a successful move). This allows us to reuse variable str if we wish (or we can ignore it, if we no longer have a use
for it).

Where else is std::move useful?

std::move can also be useful when sorting an array of elements. Many sorting algorithms (such as selection sort and
bubble sort) work by swapping pairs of elements. In previous lessons, we’ve had to resort to copy-semantics to do the
swapping. Now we can use move semantics, which is more efficient.

It can also be useful if we want to move the contents managed by one smart pointer to another.

Conclusion

std::move can be used whenever we want to treat an l-value like an r-value for the purpose of invoking move
semantics instead of copy semantics.

M.5 -- std::move_if_noexcept

https://www.learncpp.com/cpp-tutorial/stdmove/ 3/18
05/05/2021 M.4 — std::move | Learn C++

Index

M.3 -- Move constructors and move assignment

C++ TUTORIAL | PRINT THIS POST

115 comments to M.4 — std::move

« Older Comments 1 2

Michael
April 24, 2021 at 12:19 pm · Reply

Hi Alex,
hi nascardriver,

I think it would be nice if you mention that std::move is just a type casting. I checked the header file and saw that
std::move just uses a static_cast<std::string&&>(...). There is a different type in the angled bracket, I just wrote
std::string for simplicity.

Alex
April 26, 2021 at 8:30 am · Reply

Updated. Thanks for the suggestion!

Nishant
January 16, 2021 at 5:59 am · Reply

Hey,

1 #include <iostream>
2 #include <string>
3 #include <utility>
4
5 template<class T>
6 void myswap(T&& a, T&& b)
7 {
8
9 T tmp { a }; // invokes copy constructor
10 a = b; // invokes copy assignment
11 b = tmp; // invokes copy assignment
12
13 }
14
15 int main()
16 {
17 std::string x{ "abc" };
18 std::string y{ "de" };
19
20 std::cout << "x: " << x << '\n';
21 std::cout << "y: " << y << '\n';
22
23 myswap(x, y);
24
25 std::cout << "x: " << x << '\n';
26 std::cout << "y: " << y << '\n';
27

https://www.learncpp.com/cpp-tutorial/stdmove/ 4/18
05/05/2021 M.4 — std::move | Learn C++
28 return 0;
29 }

I tried this and expected the compiler to throw an error saying you can't pass an l-value to an r value reference or
something along those lines but that didn't happen.
Instead I got this:
x: abc
y: de
x: de
y: de

Which means the copy constructor did a shallow copy. Why is that?

Thanks.

Tomek
February 22, 2021 at 9:10 am · Reply

i think that tmp is referencing the same variable as a


so when you change a you also change temp

Lucky Abby
December 27, 2020 at 1:07 am · Reply

Hello nascardriver,

I don't know if this is bug or mistake I've made.


here is the code:

1 #include <iostream>
2 #include <algorithm>
3 #include <string>
4 #include <vector>
5
6 int main () {
7 std::vector<std::string> v1{"spring","summer","winter","fall"};
8 std::vector<std::string> v2(8);
9
10 std::fill(v2.begin(), v2.end(), "=");
11 std::move(v1.begin(), v1.end(), v2.begin()+2);
12
13 std::cout << "v1: ";
14 for(auto &e:v1) std::cout << e << ' '; std::cout << '\n';
15 std::cout << "v2: ";
16 for(auto &e:v2) std::cout << e << ' '; std::cout << '\n';
17
18 return 0;
19 }

as you can see that vector v2 filled with "=" and v1 already initialized with values, but surprisingly after move
operation, v1 supposed to be empty but filled with "=", the output is showing as follow:

v1: = = = =
v2: = = spring summer winter fall = =

could you please show me what is wrong here.

Thank you.

nascardriver
December 27, 2020 at 2:58 am · Reply

There's nothing wrong, this is valid behavior.


When moving from a `std::string` (Or any type in general), the moved-from object is in a valid

https://www.learncpp.com/cpp-tutorial/stdmove/ 5/18
05/05/2021 M.4 — std::move | Learn C++
but unspecified state. Reading from it is safe, but there's no way to tell what the value is (Unless specified).
By the looks of it, your standard library's `std::string` move simply swaps the `std::string`s.

https://en.cppreference.com/w/cpp/string/basic_string/operator%3D
Overload 2

Lucky Abby
December 27, 2020 at 4:11 pm · Reply

so, the meaning of unspecified is same as uninitialized, am I right?

nascardriver
December 28, 2020 at 3:10 am · Reply

Accessing an uninitialized variable causes undefined behavior. That's not the case
if the variable just has an unspecified value. You can safely read from the variable,
but it doesn't make sense because you can't predict what the value will be.

Lucky Abby
December 29, 2020 at 6:22 am · Reply

Thanks

SGN
November 23, 2020 at 5:26 am · Reply

Hi,

If r-value references bind only to rvalues, how can we pass an l-value to std::move which takes one parameter of
type r-value reference.

Thanks
SGN

nascardriver
November 24, 2020 at 7:11 am · Reply

Hello SGN

`std::move`'s parameter is an r-value-reference to a template parameter. The parameter will be an l-value


reference if you call `std::move` with an l-value, or an r-value reference if you call it with an r-value.

1 int i{};
2 std::move(i); // std::move<int&>
3 std::move(3); // std::move<int&&>

SGN
November 26, 2020 at 7:29 am · Reply

Hi,

Thanks for the reply.

I am still a bit confused. I took this from cppreference.com

Defined in header <utility>

1 template< class T >


2 typename std::remove_reference<T>::type&& move( T&& t ) noexcept;
https://www.learncpp.com/cpp-tutorial/stdmove/ 6/18
05/05/2021 M.4 — std::move | Learn C++
3 (since C++11)
4 (until C++14)
5 template< class T >
6 constexpr std::remove_reference_t<T>&& move( T&& t ) noexcept;
7 (since C++14)

If the parameter is still a r-value reference parameter, how can we bind an l-value to that?

SGN

nascardriver
November 26, 2020 at 8:50 am · Reply

There are no references to references. If you try to create an rvalue reference to an


lvalue reference, it collapses to an lvalue reference, eg.

1 using T = int&;
2
3 void fn(T&& i){} // Type of @i is int&, because there is no (int&)&&

For all collapsing outcomes, see


https://en.cppreference.com/w/cpp/language/reference#Reference_collapsing

When you have a function with an r-value template parameter like this one

1 template <class T>


2 void fn(T&& i){}

and you try to call it with an lvalue, `T` will turn into an lvalue reference to the lvalue's type
(Rather than `T` turning into the lvalue's type). Together with the rule above, the type of `i` is
`int&`.

So if we have

1 template<class T>
2 typename std::remove_reference<T>::type&& move( T&& t ) noexcept;
3
4 // ...
5
6 int i{};
7 move(i);

1. `T` turns into `int&`


2. `t` is `(int&)&&`, which collapses to `int&`
3. The instantiated template is `int&& move(int&) noexcept`

SGN
November 26, 2020 at 8:43 pm · Reply

Thanks a lot. That type deduction confused me. Now its clear.

SGN

fufufubunny
July 31, 2020 at 7:50 am · Reply

1 void myswap(T& a, T& b)

sorry but i heard that it is not advisable to pass non-const reference and then moving the objects, any idea why?or
is it the advice is wrong

nascardriver
https://www.learncpp.com/cpp-tutorial/stdmove/ 7/18
05/05/2021 M.4 — std::move | Learn C++
August 3, 2020 at 4:32 am · Reply

where'd you hear that?

F. Wu
September 20, 2020 at 6:32 pm · Reply

sorry but i heard that c++ is gay

koe
July 9, 2020 at 4:05 pm · Reply

I'm confused about how std::vector might be implemented, such that in the example above
"v.push_back(std::move(str));" invokes a move instead of a copy.

It seems like objects of type 'std::string' are statically allocated, even if internally the object manages strings
dynamically. So, in 'std::string str{"hi!"};', str is a normal object on the stack. Meanwhile, 'std::vector' contains an
array allocated on the heap. It makes sense that stack-objects can be copied over to heap-objects, and it even
makes sense that the resources managed by a stack-object could be moved over to a heap-object. However, how
would 'std::vector' accomplish that?

Here is my attempt to build a demonstration class, with '????' where I'm stuck.

1 #include <iostream>
2 #include <utility> //for std::move
3
4
5 //a dynamically allocated object, for demonstration purposes
6 template <typename T>
7 class DynamicObj final
8 {
9 private:
10 T *m_ptr{};
11
12 public:
13 //constructors
14 //default constructor; this doesn't allocate anything yet since resources may be m
oved into this object
15 DynamicObj() = default;
16
17 //normal constructor, copies the object passed in
18 DynamicObj(const T &obj) : DynamicObj{}
19 {
20 update(obj);
21 }
22
23 //normal constructor, performs a move on the object passed in (or a normal copy if
there is nothing to move)
24 DynamicObj(T &&obj)
25 {
26 //????
27 obj; //ignore compiler warning about unused variable
28 }
29
30 //copy constructor
31 DynamicObj(const DynamicObj &obj_ptr) : DynamicObj{}
32 {
33 //redirect to the copy assignment operator
34 *this = obj_ptr;
35 }
36
37 //move constructor
38 DynamicObj(DynamicObj &&obj_ptr) : DynamicObj{}
39 {
40 m_ptr = obj_ptr.m_ptr;

https://www.learncpp.com/cpp-tutorial/stdmove/ 8/18
05/05/2021 M.4 — std::move | Learn C++
41 obj_ptr.m_ptr = nullptr;
42 }
43
44 //destructor
45 ~DynamicObj()
46 {
47 //note:: this class is designed so it always points to dynamically allocated m
emory; it should never delete a pointer to a variable on the stack
48 delete m_ptr;
49 }
50
51 //overloaded operators
52 //copy assignment operator
53 DynamicObj& operator=(const DynamicObj &obj_ptr)
54 {
55 update(*(obj_ptr.m_ptr));
56
57 return *this;
58 }
59
60 //move assignment operator
61 DynamicObj& operator=(DynamicObj &&obj_ptr)
62 {
63 //self-assignment check
64 if (this == &obj_ptr)
65 return *this;
66
67 //clean up current object
68 delete m_ptr;
69
70 //move resources
71 m_ptr = obj_ptr.m_ptr;
72 obj_ptr.m_ptr = nullptr;
73
74 return *this;
75 }
76
77 //output operator: friend
78 //let's just assume it works for all possible types T this class might use
79 friend std::ostream& operator<<(std::ostream &out, const DynamicObj &obj_ptr)
80 {
81 return (out << *(obj_ptr.m_ptr));
82 }
83
84 //useful functions
85 //update; with copy semantics
86 void update(const T &obj)
87 {
88 //self-update check
89 if (m_ptr == &obj)
90 return;
91
92 //if this object hasn't been allocated yet, allocate it with the copy construc
tor of the object passed in
93 if (!m_ptr)
94 m_ptr = new T{obj};
95 else
96 //copy the object directly, overwriting whatever is there
97 *m_ptr = obj;
98 }
99
100 //update; with move semantics
101 void update(T &&obj)
102 {
103 //self-update check
104 if (m_ptr == &obj)
105 return;
106
107 //clean up current object
108 delete m_ptr;
109

https://www.learncpp.com/cpp-tutorial/stdmove/ 9/18
05/05/2021 M.4 — std::move | Learn C++
110 //move the other object (performs a normal copy if there is nothing to move)
111 //?????
112 }
113 };
114
115 //prevent dynamically allocated pointers; we don't have enough functionality for users
to properly clean them up
116 //is there a more standard way to do this?
117 template <typename T>
118 class DynamicObj<T*> final
119 {
120 protected:
121 ~DynamicObj() = default;
122 };
123
124
125 int main()
126 {
127 //works
128 DynamicObj<int> an_obj{};
129 int x{3};
130 an_obj.update(x);
131
132 std::cout << an_obj << '\n';
133
134 //works
135 DynamicObj<std::string> a_str{};
136 std::string str{"hi!"};
137 a_str.update(str);
138
139 std::cout << a_str << '\n';
140
141 //how to make this work?
142 /*
143 DynamicObj<std::string> another_str{"ho!"};
144
145 std::cout << another_str << '\n';
146
147 std::string str2{"hey!"};
148 std::cout << "str2: " << str2 << '\n';
149 another_str.update(std::move(str2));
150
151 std::cout << another_str << '\n';
152 std::cout << "str2: " << str2 << '\n';
153 */
154
155 return 0;
156 }

nascardriver
July 10, 2020 at 5:19 am · Reply

All you need is another call to `std::move` to call the r-value overloads of your functions

1 DynamicObj(T &&obj)
2 {
3 update(std::move(obj));
4 }
5
6 // ...
7
8 m_ptr = new T{ std::move(obj) };

koe
July 10, 2020 at 12:56 pm · Reply

https://www.learncpp.com/cpp-tutorial/stdmove/ 10/18
05/05/2021 M.4 — std::move | Learn C++
Thanks! That makes sense. It may be helpful to update this lesson with a comment about using
std::move on r-value references to turn them into r-values. For anyone else who may read this, I
pasted the working DynamicObj demonstration code below.

1 #include <iostream>
2 #include <string>
3 #include <utility> //for std::move
4
5
6 //a dynamically allocated object, for demonstration purposes
7 template <typename T>
8 class DynamicObj final
9 {
10 private:
11 T *m_ptr{};
12
13 public:
14 //constructors
15 //default constructor
16 DynamicObj()
17 {
18 m_ptr = new T{};
19 }
20
21 //normal constructor, copies the object passed in
22 DynamicObj(const T &obj) : DynamicObj{}
23 {
24 update(obj);
25 }
26
27 //normal constructor, performs a move on the object passed in
28 DynamicObj(T &&obj)
29 {
30 update(std::move(obj));
31 }
32
33 //copy constructor
34 DynamicObj(const DynamicObj &obj_ptr) : DynamicObj{}
35 {
36 //redirect to the copy assignment operator
37 *this = obj_ptr;
38 }
39
40 //move constructor
41 DynamicObj(DynamicObj &&obj_ptr)
42 {
43 //redirect to the move assignment operator
44 *this = std::move(obj_ptr);
45 }
46
47 //destructor
48 ~DynamicObj()
49 {
50 //note:: this class is designed so it always points to dynamically a
llocated memory (or nullptr); it should never delete a pointer to a variable
51 on the stack
52 delete m_ptr;
53 }
54
55 //overloaded operators
56 //copy assignment operator: member
57 DynamicObj& operator=(const DynamicObj &obj_ptr)
58 {
//copy the object; this may set our pointer to nullptr if copying fr
59 om an object that has been moved
60 update(*(obj_ptr.m_ptr));
61
62 return *this;
63 }
64
65 //move assignment operator: member
https://www.learncpp.com/cpp-tutorial/stdmove/ 11/18
05/05/2021 M.4 — std::move | Learn C++
66 DynamicObj& operator=(DynamicObj &&obj_ptr)
67 {
68 //self-assignment check
69 if (this == &obj_ptr)
70 return *this;
71
72 //clean up current object
73 delete m_ptr;
74
//move resources; this may set our pointer to nullptr if moving from
75 an object that has already been moved
76 m_ptr = obj_ptr.m_ptr;
77 obj_ptr.m_ptr = nullptr;
78
79 return *this;
80 }
81
82 //output operator: friend
83 //let's just assume it works for all possible types T this class might u
84 se
85 friend std::ostream& operator<<(std::ostream &out, const DynamicObj &obj
86 _ptr)
87 {
88 return (out << *(obj_ptr.m_ptr));
89 }
90
91 //useful functions
92 //update; with copy semantics: member
93 void update(const T &obj)
94 {
95 //self-update check
96 if (m_ptr == &obj)
return;
97
98 //if this object hasn't been allocated yet, allocate it with the cop
99 y constructor of the object passed in
100 if (!m_ptr)
101 m_ptr = new T{obj};
102 else
103 //copy the object directly, overwriting whatever is there
104 *m_ptr = obj;
105 }
106
107 //update; with move semantics: member
108 void update(T &&obj)
109 {
110 //self-update check
111 if (m_ptr == &obj)
112 return;
113
114 //clean up current object
delete m_ptr;
115
116 //move the other object over by attempting to use its move construct
117 or; if a move can't be performed, then the other object will be copied
118 m_ptr = new T{std::move(obj)};
119 }
};
120
121 //prevent dynamically allocated pointers; we don't have enough functionality
122 for users to properly clean them up
123 //is there a more standard way to do this?
124 template <typename T>
125 class DynamicObj<T*> final
126 {
127 protected:
128 ~DynamicObj() = default;
129 };
130
131
132 int main()

https://www.learncpp.com/cpp-tutorial/stdmove/ 12/18
05/05/2021 M.4 — std::move | Learn C++
133 {
134 //works
135 DynamicObj<int> an_obj{};
136 int x{3};
137 an_obj.update(x);
138
139 std::cout << an_obj << '\n';
140
141 //works
142 DynamicObj<std::string> a_str{};
143 std::string str{"hi!"};
144 a_str.update(str);
145
146 std::cout << a_str << '\n';
147
148 //works
149 DynamicObj<std::string> another_str{"ho!"};
150
151 std::cout << another_str << '\n';
152
153 std::string str2{"hey!"};
154 std::cout << "str2: " << str2 << '\n';
155 another_str.update(std::move(str2));
156
157 std::cout << another_str << '\n';
158 std::cout << "str2: " << str2 << '\n'; //prints 'str2: '

return 0;
}

spaceship_driver
May 20, 2020 at 11:19 pm · Reply

hey i am able to use std::move without using "utility" header in VS code.

nascardriver
May 22, 2020 at 12:16 am · Reply

That's why we included a comment in the examples

1 #include <utility> // for std::move

Note that "in VS code" provides no information about the compiler you're using. VS code is a text editor and
has no effect on the errors you're getting. I think you can see which compiler you're using in the
.vscode/c_cpp_properties.json file.

spaceship_driver
May 22, 2020 at 10:22 pm · Reply

i am using gnu compiler from linux terminal to compile my code

Constantinos
April 9, 2020 at 8:41 am · Reply

Hey,

I tried to implement the swapping of variables with a bit different way, which seems to work the same way with the
example presented here. I just wonder whether there are any essential (underlying) differences between the two
cases or if my code has any troubles in terms of cpp best practices.
The code is the following:

1 #include <iostream>
2 #include <string>
https://www.learncpp.com/cpp-tutorial/stdmove/ 13/18
05/05/2021 M.4 — std::move | Learn C++
3 #include <memory>
4
5 //forward declarations
6 //namespace declared so that no conflict with std::swap function occurs
7 namespace cjr{
8 template <typename T>
9 void swap(T&& a, T&& b);
10
11 //function overloading to check the case of l-value refereced arguments
12 template <typename T>
13 void swap(T& a, T& b);
14 }
15
16 int main(){
17
18 std::string a{"string_A"};
19 std::string b{"string_B"};
20
21 //call function for r-value arguments
22 std::cout << "Before swapping:\n";
23 std::cout << "a = " << a << "\n";
24 std::cout << "b = " << b << "\n";
25
26 cjr::swap(std::move(a), std::move(b));
27
28 std::cout << "After swapping:\n";
29 std::cout << "a = " << a << "\n";
30 std::cout << "b = " << b << "\n";
31
32 std::cout << "\n";
33
34 //call function for l-value references
35 std::cout << "Before swapping:\n";
36 std::cout << "a = " << a << "\n";
37 std::cout << "b = " << b << "\n";
38
39 cjr::swap(a, b);
40
41 std::cout << "After swapping:\n";
42 std::cout << "a = " << a << "\n";
43 std::cout << "b = " << b << "\n";
44
45 return 0;
46 }
47
48 template <typename T>
49 void cjr::swap(T&& a, T&& b){
50
51 std::cout << "Called function for r-value referenced arguments - move semantics wil
52 l be used.\n";
53 T temp{a};
54 a = b;
55 b = temp;
56 }
57
58 template <typename T>
59 void cjr::swap(T& a, T& b){
60
61 std::cout << "Called function for l-value referenced arguments - copy semantics wil
62 l be used.\n";
63 T temp{a};
64 a = b;
b = temp;
}

Thanks for this detailed and noob-friendly tutorial series! :)

Cheers,

Constantinos

https://www.learncpp.com/cpp-tutorial/stdmove/ 14/18
05/05/2021 M.4 — std::move | Learn C++

nascardriver
April 12, 2020 at 3:11 am · Reply

Careful with list initialization and templates.

1 T temp{ a };

This will not copy `a` if `T` has a list-constructor that take a list of `T`. With templates, it's better to use non-
list direct initialization.

1 T temp(a);

You're missing a call to `std::move` in line 52, it will use the copy constructor.

Use single quotes for characters. Strings are more expensive.

Constantinos
April 12, 2020 at 11:14 pm · Reply

Thank you for your answer.

I am sorry, but I am missing something: why do I need to use std::move() in line 51? I thought that
using std::move() when passing 'a' into the function in line 26 would do the work.

Since I am wrong about this, mustn't I use std::move() in the assignments in lines 53 and 54 as well?

nascardriver
April 12, 2020 at 11:39 pm · Reply

`a` and `b` are references to r-values, but they themselves are l-values. Whenever
you want to preserve the r-valueness of something, you need to use `std::move` (Or
`std::forward`).

I missed line 53 and 54, they also need `std::move`. Good point :)

Constantinos
April 13, 2020 at 4:37 am · Reply

Thanks!! Everything is clear now :)

Vissa
March 3, 2020 at 3:10 pm · Reply

Hello, just a short question. Is

1 std::move

for an object O of type T essentially equivalent to

1 static_cast<&&T>(O)

Thanks in advance

nascardriver
March 6, 2020 at 10:11 am · Reply

Yep, it's the same.

https://www.learncpp.com/cpp-tutorial/stdmove/ 15/18
05/05/2021 M.4 — std::move | Learn C++

Vissa
March 7, 2020 at 2:02 am · Reply

Then why is it that "you should not expect the state of any objects that have had
std::move() applied to be the same after they are moved"? Doesn't this just create a
temporary variable leaving the initial unchanged?

nascardriver
March 10, 2020 at 8:26 am · Reply

A cast to T&& doesn't create a copy. A function that takes a parameter by T&& is
allowed to, and expected to, modify the object. It's almost the same as passing by
reference and telling the function "Do with this object whatever you want, I won't use it anymore".

Hassan
January 23, 2020 at 2:52 am · Reply

Hello,
std::move function don't work in this exemple:

#include <iostream>
#include <string>
#include <utility>
#include <vector>

using namespace std;

template<class T>
void myswap(T& a, T& b)
{
T tmp { a };
std::cout << "a: " << a << '\n';
std::cout << "b: " << b << '\n';
std::cout << "tmp: " << tmp << "\n\n";
a = move(b);
std::cout << "a: " << a << '\n';
std::cout << "b: " << b << '\n';
std::cout << "tmp: " << tmp << "\n\n";
b = move(tmp);
std::cout << "a: " << a << '\n';
std::cout << "b: " << b << '\n';
std::cout << "tmp: " << tmp << "\n\n";
}

int main()
{
std::string x{ "1" };
std::string y{ "2" };

std::cout << "x: " << x << '\n';


std::cout << "y: " << y << "\n\n";

myswap(x, y);

std::cout << "x: " << x << '\n';


std::cout << "y: " << y << "\n\n";

return 0;
}
program prints:

https://www.learncpp.com/cpp-tutorial/stdmove/ 16/18
05/05/2021 M.4 — std::move | Learn C++
x: 1
y: 2

a: 1
b: 2
tmp: 1

a: 2
b: 1
tmp: 1

a: 2
b: 1
tmp: 1

x: 2
y: 1

but if i use std::move in "T tmp { move(a) }" the function std::move work correctly and program prints:

x: 1
y: 2

a:
b: 2
tmp: 1

a: 2
b:
tmp: 1

a: 2
b: 1
tmp:

x: 2
y: 1

I don't understand why you don't work in the first case.

nascardriver
January 23, 2020 at 3:02 am · Reply

What do you mean by "don't work". Both functions swap the values, that's what's supposed to
happen.

hassan
January 23, 2020 at 6:18 am · Reply

why values of b and tmp not empty in the first exemple Although i use the the function
std::move.

a = move(b);
std::cout << "a: " << a << '\n';
std::cout << "b: " << b << '\n';
std::cout << "tmp: " << tmp << "\n\n";

a: 2
b: 1 //b is not empty
tmp: 1

b = move(tmp);
std::cout << "a: " << a << '\n';

https://www.learncpp.com/cpp-tutorial/stdmove/ 17/18
05/05/2021 M.4 — std::move | Learn C++
std::cout << "b: " << b << '\n';
std::cout << "tmp: " << tmp << "\n\n";

a: 2
b: 1
tmp: 1 // tmp is not empty

nascardriver
January 23, 2020 at 6:25 am · Reply

Moving from a `std::string` leaves the string in an unspecified state, it may or may
not still hold a value. Even though the state is unspecified, it's a safe state and won't
lead to double frees.

For me (clang++9), your code produces

1 x: 1
2 y: 2
3
4 a: 1
5 b: 2
6 tmp: 1
7
8 a: 2
9 b:
10 tmp: 1
11
12 a: 2
13 b: 1
14 tmp:
15
16 x: 2
17 y: 1

As you can see, after every move, the string is empty (It doesn't have to be empty!).

« Older Comments 1 2

https://www.learncpp.com/cpp-tutorial/stdmove/ 18/18

You might also like