Do you have any questions about CLion? Ask the team! They will host a special Q&A session on r/cpp from 1:00-5:00 pm CET on October 29, 2024. If you are curious about the 2024.2 release, 2024.3 roadmap, CLion Nova, or anything else - don’t miss your chance! jb.gg/cl_ama_2024
Knew it already, and it was confusing at first. But it kind of forces you to follow „if you define a custom destructor, you need to define all other special functions“
2:33 In many cases it is enough to know if is_nothrow_move_constructible. This might be better than relying on a type to have move constructor specifically.
I knew this already.. but my question is why is it so. As in, why in the standard does defining one special member function implicitly change all the other special member functions? Is that something that a committee decided on or a technical necessity and if it was a decision, what's the reasoning? I ask because it's a mistake I see again and again and it seems to be an unnecessary footgun in the language. I have heard the answer "well, if you want to be defining your own destructor, then any guess the compiler might make about how you want to handle the lifetimes, copies and moves of members could be wrong, and you've told the compiler that you really care about micromanaging all of that for all of the members, so you just have to define all of that yourself" but it doesn't sit well with me because surely there's a better answer than just secretly "nope" all the other special member functions.
from stackoverflow : The rationale behind this rule was that if your code has a custom destructor, it's probably because a resource needs to be freed; and if a resource needs to be freed then the default implementation of move may leave the source object in a bogus state.
I'm pretty sure it was done for backward compatibilty with pre c++11 code. You did not have move semantics, and you do not want your legacy classes to suddenly have a (probably wrong) default move constrcutor/operator=.
@@Jack-lh6gp The reason that rationale doesn't sit well with me is because moving an object shouldn't involve calling a destructor or constructor. The whole point, or so I thought, is to optimize those operations away, basically to refer to an object in memory by another name. In essence, I would expect using `move` to be like assigning a pointer and involve calling no actual function. I suspect C++ does it in a way that's contrary to what I expect because of issues involving code generation of calls to destructors that automatically get inserted when an object's lifetime has ended, but I like my solution better even if it does involve writing more cases for code generation into the compiler.
@anon_y_mousse The problem isn't the move itself but what happens when the moved-from object's lifetime ends (i.e. what should the compiler do there by default). If there has to be a custom destructor, in many cases it's behavior has to depend on if the object has been moved from or not, but not in all (e.g. because freeing a nullptr is fine). In some cases one only had to add the move constructor while in others on has to change the destructor as well for nove semantics to work. The standard took the route with backward compatibility + complete flexibility for developers to decide how to handle the being-moved-from state of an object.
The only problem is that is_move_constructible is not what people expect. Other aspects are logical. By defining destructor, the move constructor and assignment are disabled by default for compatibility reasons. You need always define own version of move constructor if you need to benefit from moving,
@@Wimpielef I mean that Rule of 0 is "you personally write zero constructors/destructors/assigns, because they may be autogenerated if needed". But if only you write any other c-tor or d-tor, this does not happen (at least always had been). And copy/move c-tors are also constructors. And of course you need couples of respective c-tor/assign. Of course, you may skip any of: default c-tor, copy c-tor, move c-tor. But if you have copy or move c-tor, please write the respective assign operator(s).
@@besworland Oh, I see. sorry, I didn't read it as a rhetorical question, :) yes, he should make the move constructor noexcept and then detect for noexcept. needing to detect for move constructor specifically is a code smell IMO. starting from the fact that built-in and legacy classes do no have a move constructor but might have a “safe enough” copy constructor.
Do you have any questions about CLion? Ask the team! They will host a special Q&A session on r/cpp from 1:00-5:00 pm CET on October 29, 2024. If you are curious about the 2024.2 release, 2024.3 roadmap, CLion Nova, or anything else - don’t miss your chance!
jb.gg/cl_ama_2024
Knew it already, and it was confusing at first. But it kind of forces you to follow „if you define a custom destructor, you need to define all other special functions“
2:33 In many cases it is enough to know if is_nothrow_move_constructible. This might be better than relying on a type to have move constructor specifically.
I knew this already.. but my question is why is it so. As in, why in the standard does defining one special member function implicitly change all the other special member functions? Is that something that a committee decided on or a technical necessity and if it was a decision, what's the reasoning?
I ask because it's a mistake I see again and again and it seems to be an unnecessary footgun in the language.
I have heard the answer "well, if you want to be defining your own destructor, then any guess the compiler might make about how you want to handle the lifetimes, copies and moves of members could be wrong, and you've told the compiler that you really care about micromanaging all of that for all of the members, so you just have to define all of that yourself" but it doesn't sit well with me because surely there's a better answer than just secretly "nope" all the other special member functions.
from stackoverflow :
The rationale behind this rule was that if your code has a custom destructor, it's probably because a resource needs to be freed; and if a resource needs to be freed then the default implementation of move may leave the source object in a bogus state.
I'm pretty sure it was done for backward compatibilty with pre c++11 code. You did not have move semantics, and you do not want your legacy classes to suddenly have a (probably wrong) default move constrcutor/operator=.
@@Jack-lh6gp The reason that rationale doesn't sit well with me is because moving an object shouldn't involve calling a destructor or constructor. The whole point, or so I thought, is to optimize those operations away, basically to refer to an object in memory by another name. In essence, I would expect using `move` to be like assigning a pointer and involve calling no actual function. I suspect C++ does it in a way that's contrary to what I expect because of issues involving code generation of calls to destructors that automatically get inserted when an object's lifetime has ended, but I like my solution better even if it does involve writing more cases for code generation into the compiler.
@anon_y_mousse The problem isn't the move itself but what happens when the moved-from object's lifetime ends (i.e. what should the compiler do there by default). If there has to be a custom destructor, in many cases it's behavior has to depend on if the object has been moved from or not, but not in all (e.g. because freeing a nullptr is fine). In some cases one only had to add the move constructor while in others on has to change the destructor as well for nove semantics to work. The standard took the route with backward compatibility + complete flexibility for developers to decide how to handle the being-moved-from state of an object.
@@Spielix Tell me you didn't read my entire post without telling me.
The only problem is that is_move_constructible is not what people expect. Other aspects are logical. By defining destructor, the move constructor and assignment are disabled by default for compatibility reasons. You need always define own version of move constructor if you need to benefit from moving,
Rule of 0 is good until you need to parametrize the construction. How often the default init is enough outside the laboratory?
Rule of 0 is not about the constructor, only about copy/move/assignment/destructor
@@Wimpielef I mean that Rule of 0 is "you personally write zero constructors/destructors/assigns, because they may be autogenerated if needed". But if only you write any other c-tor or d-tor, this does not happen (at least always had been). And copy/move c-tors are also constructors. And of course you need couples of respective c-tor/assign. Of course, you may skip any of: default c-tor, copy c-tor, move c-tor. But if you have copy or move c-tor, please write the respective assign operator(s).
What about marking move constructor with noexcept? It might also end up with interesting behavior.
If you do that consistently (and you should) then it can be reliably detected with is_nothrow_move_constructible as I say in the other comment.
@@AlfredoCorrea agree, my point was more about that it can break move semantics and this point is worth to mention.
@@besworland Oh, I see. sorry, I didn't read it as a rhetorical question, :) yes, he should make the move constructor noexcept and then detect for noexcept. needing to detect for move constructor specifically is a code smell IMO. starting from the fact that built-in and legacy classes do no have a move constructor but might have a “safe enough” copy constructor.
I know Howard Hinnant's table, Nevertheless the rules are confusing
I wish they fixed header units :/