Тёмный

Re-inventing move semantics in modern C++ in 13 minutes 

Code for yourself
Подписаться 3,4 тыс.
Просмотров 6 тыс.
50% 1

Move semantics, value semantics, rvalue references, rvalues, lvalues etc. are all terms related to what lies at the heart of modern C++. These concepts are all related and in this video we look at exactly what they mean. By re-inventing them.
In this video we re-invent move semantics as it exists in C++11 starting from the reasons for its existence and inventing every single machinery that is used to achieve the proper moving of the data between objects along the way.
📚 As always, the script to the video lives here: github.com/cpp-for-yourself/s...
Related materials:
- Value categories: en.cppreference.com/w/cpp/lan...
- std::move: en.cppreference.com/w/cpp/uti...
Hope you enjoy this one! Please leave any suggestions below the video or tell what you liked about the video!

Опубликовано:

 

28 июн 2024

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 42   
@evgenysenkin2859
@evgenysenkin2859 Год назад
Great explanation! I highly recommend the book Effective Modern C++ by Scott Meyers for a deep understanding of how it works under the hood.
@CodeForYourself
@CodeForYourself Год назад
Yes, I second this recommendation. It is a bit advanced though but great nevertheless.
@CyrillStachniss
@CyrillStachniss Год назад
“there is no black magic.” 😍
@CodeForYourself
@CodeForYourself Год назад
Yep, you can guess who was my inspiration for this 😉
@Evan490BC
@Evan490BC Год назад
There is no black magic but there is certainly _bad naming_ , as std::move doesn't move std::forward doesn't forward, as Scott Meyers has said...
@CodeForYourself
@CodeForYourself Год назад
Yeah, the naming could be done better. But then naming things is hard. To it's credit, std::move was a good enough name to make all the people involved in it's design and implementation understand each other. 🤷‍♂️
@harleyspeedthrust4013
@harleyspeedthrust4013 7 месяцев назад
This was a great video! Thanks man, I subscribed
@CodeForYourself
@CodeForYourself 7 месяцев назад
Thanks 🙏 happy that you liked it!
@zamf
@zamf Год назад
Great video once again. I have one small addition: when declaring a move constructor and a move assignment operator it is a good idea to mark is as `noexcept`. Most of the time this should be possible because you're just moving some pointers around. I think main the reason for this is the MSVC compiler which (for some odd reason) can still call the copy constructor even when you do a std::move if the move constructor is not marked as noexcept. Generally, it is not required but in most cases it is possible and it is a good coding practice. A good follow up episode could be perfect forwarding, which is basically extending value semantics to generic types. The problem is that in order to understand perfect forwarding one must first understand templates, which is a huge topic on it own.
@CodeForYourself
@CodeForYourself Год назад
Thanks Zamfir! I did not know about the MSVC quirk! 😳 Do you have a link for me to read more about it? That sounds really interesting! As for noexcept I did not include it because we did not talk about exceptions yet 🤷‍♂️ And you're right about perfect forwarding: we need templates for that. It's coming but later 😬
@artemmentiy7107
@artemmentiy7107 8 месяцев назад
Great lesson, it really helped me! + bonus points for cool card tricks
@CodeForYourself
@CodeForYourself 8 месяцев назад
Glad I could help!
@abzrg
@abzrg Год назад
Thanks a lot! Do you have any plan on touching on forwarding/universal references? How much do you use them on your daily work? I sort of feel it's mostly used by library developers Edit: Oh I read the comments; you do. :)
@CodeForYourself
@CodeForYourself Год назад
That is a great question! I will cover forwarding later as I believe it is a much more advanced thing. And yes, I would agree that this is mostly for library development. I did use it a bunch of times but rarely in production code to be honest 😬
@puppergump4117
@puppergump4117 Год назад
Wish I saw this video a week ago, I was having trouble with objects disappearing from vectors after they move in memory. The solution for me was to use a copy constructor, because the constructor isn't called when the object is copied.
@CodeForYourself
@CodeForYourself Год назад
You can blame my laziness if you like. 😉 We can always agree that I should release these things quicker 😬
@wChris_
@wChris_ 7 месяцев назад
I always thought of rvalues as temporary values, which are destroyed as soon as they go out of scope. This is why you can "scavange" its resources. But unfortunately how c++ works is that rvalues can bind to lvalues, basically violating that assumption. Its making a possibly short lived object live longer than it actually is. This will cause them to become possibly dangling references!
@CodeForYourself
@CodeForYourself 7 месяцев назад
Sorry, I’m a bit lost, could you maybe give an example of what you mean?
@wChris_
@wChris_ 7 месяцев назад
@@CodeForYourself With current c++ rules, this would be allowed to compile: auto even = std::vector {1,2,3} | std::views::filter([](int i) { return i%2 == 0; }); ranges are evaluated lazily, thus the ranges filter will hold an lvalue reference to the vector. with current c++ rules this would be allowed as rvalues can be extended to lvalues. But compilers are smart and this does not compile since the filter will hold a dangling reference. Lets talk about temporary lifetime extension. I can use temporary lifetime extension to store either a value or a reference in a reference So thanks to temporary lifetime extension i can this: A const& a = A(); or this: A const& a = get_A(); // where get_A( ) returns a reference. this will hold rvalues just fine as well as regular values trough Temporary lifetime extension. (In a way auto const& is a universal storage for any value or reference.) Consider a getter for some struct A, that returns a const A&&, so can steal its contents if needed. Or use std::move. const A& a = std::min(a1.getA(), a2.getA()); (assume As are comparable) The input of std::min are 2 rvalues references and due to the implementation of std::min they accepted as lvalues and thus get returned into lvalue references, and will also be returned as lvalue reference, as far as temporary lifetime extension is concerned there is nothing to extend the lifetime of. This means a dangles. Maybe we can write our own min using universal references and forward them out of min. And if both of them are rvalue references we can still steal their resources after min if needed. but the problem is a still dangles. you can assign an rvalue reference to an lvalue reference but since its actually an rvalue reference it gets destroyed and you get the dangling reference. Consider a getter for some struct A, that either returns a an A, a const A&. (Or even a A const&&) Ideally in some generic code i want to hold either the value or the reference. decltype(auto) foo() { const A& a = get_A(); } if get_A() returns a value, we get a dangeling reference due to it being an rvalue. and using auto foo() { const A& a = get_A(); } this will always copy, because auto is always a value. This is a problem due to Temporary lifetime extension lying about its value. As far as the type system is concerned get_A() always returns a reference. When in fact it could be a value. This could actually be solved by making it illegal to bind rvalue references to lvalue references.
@gordonfreimann
@gordonfreimann Год назад
i wish the compiler didnt allow us to use the object after reference move operation
@CodeForYourself
@CodeForYourself Год назад
Yeah, it would be cool. But there is a reason why it is done the way it's done. We would have to record the fact that the data was moved from somewhere 🤷‍♂️
@zamf
@zamf Год назад
The main problem is when you move data conditionally. if(something) a = std::move(b); After this point you may or you may not use `b` and the compiler cannot help you because the decision happens at runtime. But I agree that in cases where the compiler *can* prove that the object is moved-from then it should print a warning or something. Actually, you can still use a moved-from object but there are only 2 allowed operations: destruction and assignment. Meaning you can safely destroy the "empty" object or you can give it a new meaningful value by assigning to it.
@kevindelnoye9641
@kevindelnoye9641 Год назад
Clang-tidy bugprone-use-after-move can help sometimes
@MarekKnapek
@MarekKnapek Год назад
​@@CodeForYourself Not a good idea, how would you swap two variables? Currently, it is legal to access an object after it is moved from, it should be in valid, but unspecified state. That means, it could be assigned to, and all const member functions could be called on it (because they have no pre-condition on whether they could be called or not). Many standard library types are in valid and in SPECIFIED state after being moved from, typically in an empty state. Consider following example. int main() { huge_type a; huge_type b; huge_type tmp; a = make_something_huge(1); b = make_something_huge(2); /* swap begin */ tmp = std::move(a); a = std::move(b); /* oh no, use-after-move of variable a according to your rules */ b = std::move(tmp); /* oh no, use-after-move of variable b according to your rules */ /* swap end */ assert(tmp.is_empty()); /* oh no, use-after-move of variable tmp according to your rules */ } Remember, assignment operator is just syntactic sugar for a member function. You can imagine the previous example using a.assign(std::move(b)) instead. What would be better is destructive move. After an object is moved-from it is destroyed (its destructor is being run) and although its name is still in scope, referring to it is a compiler error. Example: int main() { huge_type a; huge_type b; a = make_something_huge(1); b = make_something_huge(2); a = std::destructive_move(b); b = make_something_huge(3); /* oh no, compiler error, b was already destroyed, according to my rules */ }
@puppergump4117
@puppergump4117 Год назад
And I wish it didn't let me check if an unsigned int is less than 0, but good practices prevent these problems.
@christer8964
@christer8964 Год назад
That was a really nice cake.
@CodeForYourself
@CodeForYourself Год назад
Yes! My mom made it for my PhD defense 😄
@christer8964
@christer8964 Год назад
@@CodeForYourself Such a perfect PhD cake. And I guess it is actual unfolded pages from your thesis.
@CodeForYourself
@CodeForYourself Год назад
Yep, exactly. I still have no idea how she pulled it off!
@Acetyl53
@Acetyl53 Год назад
Constructive criticism. I'm not sure if it's to mask a blend + jump cut, but I would recommend not doing that zoom things that's become so popular lately. I can tell it's not your intention, but I find it oddly and indeed, shamelessly domineering yet sneaky. It's just really kind of gross even if it's meant for emphasis. Though my brain is processing this video as a flat image with inferred depth (your head becomes larger), fact is, IRL if that was happening it would mean one of us suddenly leaned in closer to the other's face, then back, then lean, then back. It's weird. Don't know who started it, but I dislike it. A lot. When weird and mediocre is the norm, different may well be simply better. Be different. Resist decay.
@CodeForYourself
@CodeForYourself Год назад
I see the point you're making here and mostly agree with it. The amount of zooming in is over excessive in this video. I will probably settle somewhere in the middle in future. I still like how zoom works for important moments but will try not to use indiscriminately.
@Acetyl53
@Acetyl53 Год назад
@@CodeForYourself I think, if we try to put it in practical and mechanical terms, a primary issue is in how activation of threat processing interacts with working memory and other short term memory state. A lot of research shows, regardless of context, there is a hard bias towards interpreting any object approaching as potentially threatening. The unpredictability and suddenness of the zoom acts as a neurological "interrupt" signal, which eill either rapidly store existing state in a poorly structured form, or will dump existing state. It's not as extreme as suddenly registering that you're looking into the face of a lion or something through the tall grass, but the underlying fundamentals are there. Personally I think high contrast works better for emphasis than the startle response. So information is easily organized into a tree-like dendritic structure, which naturally fits into the associative web-like structure of long term storage.
@puppergump4117
@puppergump4117 Год назад
I think you're trying to read into it too much. He probably thought just sitting there staring at him talk would be boring, and he didn't think music would make it better, so he went for some different edits. I recommend using Sebastian Lague's style. He's basically seamless with his edits.
@Acetyl53
@Acetyl53 Год назад
@@puppergump4117 Nah, I don't mean to say all of this was driven by some grand intent or deliberate contrivance. I'm just talking about the ultimate effect, regardless of its genesis.
@puppergump4117
@puppergump4117 Год назад
@@Acetyl53 I didn't notice it though. But luckily, you don't have to watch the videos to get all of the information if it's a problem.
@tridivbhatt
@tridivbhatt Год назад
How many tries did the card shuffle take? 🤔 😆
@CodeForYourself
@CodeForYourself Год назад
I'll just say many. But I learned how to do it long time ago. Long enough to forget already 😬
Далее
Barry Policeman And His Son Vs Prisoners
00:26
Просмотров 991 тыс.
lvalues and rvalues in C++
14:13
Просмотров 304 тыс.
C++ 11: Rvalue Reference -- Move Semantics
14:22
Просмотров 148 тыс.
Const correctness in C++
10:02
Просмотров 2,6 тыс.
C++ Move Semantics and R-Value References
14:16
Просмотров 6 тыс.
CMake - the essential package
27:54
Просмотров 9 тыс.
C++ classes: the basics
13:24
Просмотров 1,1 тыс.
std::move and the Move Assignment Operator in C++
16:06