You, sir, have some of the clearest explanations of the complicated C++11+ examples I've seen - and I've read a lot of them. Thank you for taking your time to make these, I will recommend your channel to others learning C++.
Bo, I'm not sure if it was mentioned in the history of comments, but should you ever redo this (very nice) video, you might want to discuss copy elision. Some viewers may be at a loss to explain why their move constructor does not get called because the compiler is allowed to elude copying (darn smart compilers). Nice work though... Love your videos!
the delete shouldnt be much of a problem as the same amount of memory is being deleted and since double is a built in type, there will not be any memleaks. However, it is good practice to use delete[] if new [] was used. Also, a check to delete will be more costly so there is no need to do it.
@Anand Kulkarni It's not needed to check if (nullptr == arr_) before delete [] arr_, because if arr_ is nullptr, then delete [] arr_ will do nothing. If we assume that mentioned destructor is called many times (let's say millions), then code without if (nullptr == arr_) will be faster.
Excellent explanation: 1) Explains what lvalue and rvalue are 2) Explains what lvalue reference and rvalue reference are. 3) Explains how they enable function overloading => they will also result in constructor overloading 4) Introduces Move constructor (that will be called implicitly now instead of Copy constructor when dealing with rvalues) 5)
at 13:05, you say it is ok to call hoo(string s); no matter how big the string is. How? If I write string s = "askjo;sjedfselkajs;dlfijasdf"; hoo(s); isn't a full copy being performed here?
9:06 foo_by ref() is accepting a reference, thus it expects an lvalue, createBoVector() will return an rvalue, in this case, shouldn't we have compile error ?
After doing some trials with gcc I think that the behaviour of this code is very compiler specific foo(createBoVector()) at 06:00 does not call the copy ctor and after adding the move ctor at 08:00 it does not call it either
@4:07: slightly offtopic, why don't we need to release the data that arr_ was (potentially) pointing to, at the beginning of the copy constructor, before we assign it a new array of doubles?
Hey Bo, shouldn't the signature of the foo_by_ref function be "foo_by_ref(const boVector& v)", as the standard says that rvalues can only be bound to const references? Timestamp being 9:02. Thank you!
At 6:27 (it's even earlier, but this is when my question occured): Why would we want to use a move constructor, when we could just make the foo() function to accept the boVector object as a const &?
Probably late to this, but in that case, foo() will modify the boVector, so it can't be const and then you would just be taking an lvalue reference. Here's a short overview as far as I understand it: value: Copy the parameter into a temporary variable which gets destroyed after the function ends reference: Take an lvalue by reference - values can not be used as a parameter const reference: Take both lvalues and rvalues as references. However since they are const, neither can be modified later on Rvalue reference: Can take rvalues by reference and modify them Hope I could clear up confusion :D
class boVector should have destructor with "delete [] arr_;" statement instead of "delete arr_;" as you use "new[]" but not "new" to allocate memory. and its size shold be represented by a variable of type size_t instead of int :( nice explanation of move semantics though
#Adrian Yang they are not both ok, if you call delete without square brackets you are only deleting the first element pointed by the double* which corresponds to arr_[0], instead of deleting the whole array, thus delete[] is the only right way to avoid a memory leak in your program (or even better, smart pointers)
A funny thing I just found is, if I add cout in both copy an move constructor for this code, in gcc 4.8, there's no move constructor message printed, but in MS VS2010 compiler, move constructor message printed
lvalue is simply locator value, i.e. an object that can be located in the memory. And, rvalue is register value which is computed and the result is placed in some register temporarily in CPU. So, rvalue is such an object which cannot be located in memory
Your explanation is 99 percent correct, but still 1 percent is missing. glvalue, lvalue, xvalue, prvalue, constant expression, volatile, and lots of other advanced C++ constructs are simple and crystal clear if we look them from the Assembler language level. Since C++ Standard Committee are trying to explain a certain concept in C++ to non Assembler language programmers, they have to make up weird terminologies, such as glvalue, xlvalue, prvalue, even constant expression... unevaluated expression. All these concepts in C++ are trivial and do not need any explanations in Assembler language level. Anyone who has some experience in programming using Assembler language can readily understand what all weird terminologies are saying. C++ language is advancing both in high-level and low-level constructs.
How are you accessing rhs's private pointer variable arr_ from the move constructor? I though another objects private variable should not be accessible, or is this something to do with it being a rvalue? I'm new to c++, used to java so that might be why i am confused. Any answers are welcome, thanks.
In c++ every class method (function) has access to private fields (of the same class). Constructor is just one of the class methods. One more thing: access rights in c++ refers to classes, not the specific objects. Heard that in Java access rights is limited only to objects.
Hi. I have a question. I can use the lvalue reference and do the same (in constructor). Move the object. So why do we need special rvalue reference in your use case?
+Jeremy Ley Two reasons: 1. It's a stricter contract. The function tells you that it won't mutate the object just by its signature. 2. If your value is already const, then you're still allowed to call this function. If it expected a mutable reference then you'd need to use an ugly `const_cast(val)` to remove the constness. You should always add const to your references (unless you really mutate). So, james k really should say "should" not "has to".
Can someone explain to me how the foo function actually applies the constructor to the class object parameter? I don't see a connection between the foo function and the constructors he included.
It is because the bovector is being passed by value. When you pass by value, the parameter is its own separate instance of the object. The compiler will automatically call an object's copy constructor when passing by value so that the parameter object has the same data of the object that was passed to the function. If you pass by reference, no copy needs to be made, so the compiler will not call the constructor.
Hi Bo Qian, your explanation is very clear. However, how do I really know which constructor is called? I have tried to add a printing sentence in each constructor (i.e., copy constructor and move constructor). However, when I am passing by a lval, the printing message (in the copy constructor) is printed, while if I passed a rval, no message is printed.
First, thank you for making these videos. I have found them to be beneficial. You mentioned that the the STL container use the move semantics. You gave an example of a function returning a vector by value. Since we already have Return Value Optimization, is there an advantage to using the move semantics? Should I use move semantics for my own objects rather than relying on RVO?
its not very good to give such a poor written Codesnippet where you do not explain the function signatures which are very much needed to understand this topic. Bad tutorial.
Compared to other C++ tutorials, this one clearly has the touch of a master.
absolutely, this is the best explanation that i have seen for this otherwise bizare concept. thanks!
You, sir, have some of the clearest explanations of the complicated C++11+ examples I've seen - and I've read a lot of them. Thank you for taking your time to make these, I will recommend your channel to others learning C++.
oh my god, thank you so much... this makes so much more sense than the 250 lines of code my textbook tried to use to explain this with shoddy prose.
Can we just acknowledge how much of a legend this guy is. These tutorials are honking good!
Thank god someone who explained the usage of move constructors without giving the example of push_back for vectors!
great tutorial, very clearly illustrated, much better than many so called professor.
No kidding, this is the first time this made perfect sense to me, thank you sir. I subscribed to your channel
Bo, I'm not sure if it was mentioned in the history of comments, but should you ever redo this (very nice) video, you might want to discuss copy elision. Some viewers may be at a loss to explain why their move constructor does not get called because the compiler is allowed to elude copying (darn smart compilers). Nice work though... Love your videos!
lol, -fno-elide-constructors, it takes me a long while to figure this out.
THANK YOU!! (and to Youcheng Lin also!)
This was confusing the hell out'ta me!
If you are reading this, then also check out the requirement of copy elision on C++ 17
Simple and clear explanation about move semantics! Thanks Bo! :)
Good tutorial Bo. However, one mistake: the destructor here is not releasing all the memory in the array. It should be:
~boVector { delete [] arr_; }
+AJICams I was wondering about that too, thanks for clearing that up!
plus destructor should not delete if arr_ is nullptr ; i know it wont crash program but does not look sensible to delete a nullptr value.
the delete shouldnt be much of a problem as the same amount of memory is being deleted and since double is a built in type, there will not be any memleaks. However, it is good practice to use delete[] if new [] was used. Also, a check to delete will be more costly so there is no need to do it.
@Anand Kulkarni It's not needed to check if (nullptr == arr_) before delete [] arr_, because if arr_ is nullptr, then delete [] arr_ will do nothing.
If we assume that mentioned destructor is called many times (let's say millions), then code without if (nullptr == arr_) will be faster.
exactly.
Excellent explanation:
1) Explains what lvalue and rvalue are
2) Explains what lvalue reference and rvalue reference are.
3) Explains how they enable function overloading => they will also result in constructor overloading
4) Introduces Move constructor (that will be called implicitly now instead of Copy constructor when dealing with rvalues)
5)
at 13:05, you say it is ok to call
hoo(string s);
no matter how big the string is. How? If I write
string s = "askjo;sjedfselkajs;dlfijasdf";
hoo(s);
isn't a full copy being performed here?
+fatichar Yep, my thought exactly. There's really no reason not to use const reference for objects whenever you can.
Share the same point with you.. Bo needs to be more specific when making conclusions.
I think it makes sense only in case you call it like this
hoo(std::move(s));
extremely good....invaluable listening.keep it up brother.
I love your tutorials. They are clear and excellent paced.
finally the video i was looking for !!!! u r my hero
This was fantastically helpful, thanks Bo!
The call to foo_by_ref at 8:55 does not work as we are trying to bind a rvalue to a reference which is not allowed by the compiler.
9:06 foo_by ref() is accepting a reference, thus it expects an lvalue, createBoVector() will return an rvalue, in this case, shouldn't we have compile error ?
After doing some trials with gcc I think that the behaviour of this code is very compiler specific
foo(createBoVector()) at 06:00 does not call the copy ctor
and after adding the move ctor at 08:00 it does not call it either
add this compiler flag, -fno-elide-constructors, to remove the default optimization done by the compiler
@4:07: slightly offtopic, why don't we need to release the data that arr_ was (potentially) pointing to, at the beginning of the copy constructor, before we assign it a new array of doubles?
@ 8:57 , I think it should have been: foo_by_value(createBoVector()) and foo_by_ref(reusable) , can't bound an r-value to l-value reference!
Hey Bo, shouldn't the signature of the foo_by_ref function be "foo_by_ref(const boVector& v)", as the standard says that rvalues can only be bound to const references? Timestamp being 9:02. Thank you!
awesome video. I"ve been learning cpp b/c of the new work. And lots of nuances that I need to learn from a java background.
Thanks a lot for such excellent content with a beautiful explanation. Kindly share other latest versions too.
Good explanations on the move semantics ! Thank you !
this video was an ablosute eye opener! thank you so much :)
At 6:27 (it's even earlier, but this is when my question occured): Why would we want to use a move constructor, when we could just make the foo() function to accept the boVector object as a const &?
Probably late to this, but in that case, foo() will modify the boVector, so it can't be const and then you would just be taking an lvalue reference.
Here's a short overview as far as I understand it:
value: Copy the parameter into a temporary variable which gets destroyed after the function ends
reference: Take an lvalue by reference - values can not be used as a parameter
const reference: Take both lvalues and rvalues as references. However since they are const, neither can be modified later on
Rvalue reference: Can take rvalues by reference and modify them
Hope I could clear up confusion :D
I have always just used const references
but I might be able to find a use for this, I just don't see any reason to force it's use in my code
Hello @6:45 the destructor must have delete arr_ [] instead of delete arr_
foo_by_value and foo_by_ref are marked incorrectly in the video @8:51 time.
Best explanation, Thanks God! Pls make more vedioes
class boVector should have destructor with "delete [] arr_;" statement instead of "delete arr_;" as you use "new[]" but not "new" to allocate memory.
and its size shold be represented by a variable of type size_t instead of int :(
nice explanation of move semantics though
am... destructor should be delete [ ] arr_; right?
+bunc11 I think both delete arr_ and delete [] arr_ are OK, because the type of arr_ is double*, although delete [] arr_ is better.
#Adrian Yang they are not both ok, if you call delete without square brackets you are only deleting the first element pointed by the double* which corresponds to arr_[0], instead of deleting the whole array, thus delete[] is the only right way to avoid a memory leak in your program (or even better, smart pointers)
right, new - delete, new[] - delete []
I tried deleting an array on Ubuntu and valgrind shows no resource leak. So they should be both fine.
@@Ben-os7if It's an undefined behavior tho
8:56 wait how can foo_by_ref(createBoVector()) work if it takes an lvalue ref. and createBoVector() is an rvalue?
Yes, you are right. fuck him
@@fredwu6812 But you don't have to be so rude.
How does this syntax being ok? "rhs.arr_ = nullptr", while before hand the pointer "arr_ = rhs.arr" is pointing to it?
Thanks for this videos! Unitl now I was creating move constructors like it was alchemy
Very nice explanation. Thanks.
13:04 Old Transformers cartoon on in the background.
Great video
Thank u for your clear explanation, it was very educational.
Thank you! Great explanation!
When you assign 'nullptr' to rhs.arr_, doesn't that mean that the rhs object will remain in the memory?
Yes. Because it need to be used by the function foo, and it will be destroyed after foo function ends
binding rvalue to lvalue reference will cause compile error. should use const lvalue reference.
rhs is a lvalue in boVector(boVector&& rhs) ?
With g++ passing a rvalue will perform a copy elision. So we need to disable copy elision in order to see a move constructor called.
A funny thing I just found is, if I add cout in both copy an move constructor for this code, in gcc 4.8, there's no move constructor message printed, but in MS VS2010 compiler, move constructor message printed
good video, very well explained
lvalue is simply locator value, i.e. an object that can be located in the memory. And, rvalue is register value which is computed and the result is placed in some register temporarily in CPU. So, rvalue is such an object which cannot be located in memory
Your explanation is 99 percent correct, but still 1 percent is missing. glvalue, lvalue, xvalue, prvalue, constant expression, volatile, and lots of other advanced C++ constructs are simple and crystal clear if we look them from the Assembler language level.
Since C++ Standard Committee are trying to explain a certain concept in C++ to non Assembler language programmers, they have to make up weird terminologies, such as glvalue, xlvalue, prvalue, even constant expression... unevaluated expression. All these concepts in C++ are trivial and do not need any explanations in Assembler language level. Anyone who has some experience in programming using Assembler language can readily understand what all weird terminologies are saying.
C++ language is advancing both in high-level and low-level constructs.
Clear and clean. Thank you!
Very Nice tutoriar.
Thank you for making the video. I learned a lot from it.
very nice video. Thanks so much. Clear some doubts of mine.
bo you are an amazing teacher keep up the good work!
Great Video! I understand a lots with you. Thanks so much! (Not important but just mention that I agree with Taras)
How are you accessing rhs's private pointer variable arr_ from the move constructor?
I though another objects private variable should not be accessible, or is this something to do with it being a rvalue?
I'm new to c++, used to java so that might be why i am confused.
Any answers are welcome, thanks.
In c++ every class method (function) has access to private fields (of the same class).
Constructor is just one of the class methods.
One more thing: access rights in c++ refers to classes, not the specific objects.
Heard that in Java access rights is limited only to objects.
No you can't access an modify the value of rhs.arr_ , it's a mistake
Yes you can. I even just tried it.
Good explanation of move semantics.
Excellent explanation of this not so easy to understand topic.
8:00 move constructor will not be called. I've tried it myself.
Please try to compile the code with -fno-elide-constructors option. Move constructor will be called then.
Would be wonderful to upgrade this series to c++20
I have one question, is this useful when you have static arrays?
Can anyone please explain me how constructor is calling with the function calling ?
Thank you. This helps tremendously. 10/10
Hi. I have a question.
I can use the lvalue reference and do the same (in constructor). Move the object. So why do we need special rvalue reference in your use case?
I think the function call would be ambiguous then.
minor correction the signature of foo_by_ref has to be 'const boVector&' , thats fine as focus here is on move constructor.
Why must it be const? I see this everywhere but don't understand why it must be immutable.
+Jeremy Ley Two reasons:
1. It's a stricter contract. The function tells you that it won't mutate the object just by its signature.
2. If your value is already const, then you're still allowed to call this function. If it expected a mutable reference then you'd need to use an ugly `const_cast(val)` to remove the constness.
You should always add const to your references (unless you really mutate). So, james k really should say "should" not "has to".
Can someone explain to me how the foo function actually applies the constructor to the class object parameter? I don't see a connection between the foo function and the constructors he included.
It is because the bovector is being passed by value. When you pass by value, the parameter is its own separate instance of the object. The compiler will automatically call an object's copy constructor when passing by value so that the parameter object has the same data of the object that was passed to the function.
If you pass by reference, no copy needs to be made, so the compiler will not call the constructor.
Excellent explanation Bo, thanks!
Why not just use pass by const reference instead of the move semantics?
At 04:08, usually when you allocate memory with "new []" then you should use "delete []" operator, is there any chance of memory leak in this code?
Probably just made a mistake
Hi Bo Qian, your explanation is very clear. However, how do I really know which constructor is called?
I have tried to add a printing sentence in each constructor (i.e.,
copy constructor and move constructor). However, when I am passing by a lval, the printing message (in the copy constructor) is printed, while if I passed a rval, no message is printed.
For that use debugging, set a breakpoint and step through the code.
when we use rhs.arr_ = nullptr; it doesn't delete the elements in heap? Dont we have to use delete rhs? and then assign nullptr to rhs.arr_?
thanks
First, thank you for making these videos. I have found them to be beneficial.
You mentioned that the the STL container use the move semantics. You gave an example of a function returning a vector by value. Since we already have Return Value Optimization, is there an advantage to using the move semantics? Should I use move semantics for my own objects rather than relying on RVO?
+Peterolen Thanks make sense. Thank you for the reply.
Fucking fantastic explanations my dude, I'm super glad I found your video :)
Very nice video. Thanks !!
In boVector size is a private variable...how can you do rhs.size in the copy constructor
It is private to the class 'boVector' not to the object 'rhs'
Great video, again:)
it would've been better if you replaced the boVector example with a *working* one.
How would you implement those functions?
Nicely done.
2:16
std::move() converts lvalue to rvalue.
12:53
the destructor of boVector should call 'delete[] arr_', not 'delete arr_'.
excellent
thanks a lot Bo !!!
Thank you very much!!
where can I download the ppt?
Excellent
you could pick another name for variable 'a'. Its kinda confusing when you writing two 'a' in comment section.
great ,thanks !
This tutorial is fucking great.
You should really run your code time to time to show that it is working. In my code, both foo methods called copy constructor.
9:46 хехееее distroy 😊
Wouldn't it be easier to just use pointers?
Bruno Carlos Caetano Santos .
Pointers are evil mmmkay?
***** no
chika boom ok
lost me after 8:00
It's hard to talk about this when you can't pronounce R nor L
For anyone need refresher on rvalue and lvale, here is link to video by Bo Qian
ruclips.net/video/UTUdhjzws5g/видео.html
say what again? lvalue leflence
+salex2500 The only embarrassing thing here has been said by you.
salex2500 are you stupid?
How to goal kick lmao
its not very good to give such a poor written Codesnippet where you do not explain the function signatures which are very much needed to understand this topic. Bad tutorial.
You sound like erchito