-
Notifications
You must be signed in to change notification settings - Fork 260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SUGGESTION] Only apply move from last-use behaviour to @basic_value
and well-known STL types (or appropriate types detected at compile-time)
#1030
Comments
I am on the opposite side of the coin: I would prefer the automatic moves to be opt-out. I really like Cpp2's embracement of value semantics, and to be honest it works most of the time so far (perhaps biased view on my side?). Let me ask directly: What do you (and others) think about using Finally, I'd like to add that AFAIK, we can only rely on local scope analysis for this, so adding metafunctions or attributes outside the function where the problem occur is probably not gonna work. And I wouldn't like having to mark/annotate these things anyway; It should be as automatic and efficient as possible by default. |
Thanks @bluetarpmedia ! Your "value type" is a great observation, I think you hit the nail on the head. Move from last use is primarily for value types. I just did a test implementation to do last-use moves only from copyable types, and found that this path:
However, there is one consequence: Move-only types now need to say Consider
Is requiring this 'move' good or bad? On the "good" side, it's more explicit; So on the one hand, it seems to be that move-only types are designed to be moved around, so should be move candidates. While there will be some APIs like But I think the updated story is still consistent, and I hope easy to teach: "Copyable objects get the optimization of getting automatically treated as a move-candidate from their definite last use." For move-only types it wouldn't be an optimization, and you should still write WDYT? |
I think this is for sure better than what I proposed, requires less annotation overall. Also, while I think having to write the move for move-only types is redundant, I'm also aware that this was already what we were doing for things like unique_ptrs and locks, so less delta with C++. |
Nice work Herb, this is a great solution! I think it's a positive sign that this change resolved a bunch of issues but had minimal impact on the test cases (in a wider sense; obviously one in particular required lots of changes.) For copyable objects which also provide move semantics, move is an optimisation for copy, so it makes sense to me that this happens implicitly where possible. But for move-only objects, move cannot be an optimisation for copy, so the explicit syntax makes sense to me here. I feel like this is actually an improvement over C++, because in C++ we write
|
Sound nice and having the move for move-only types there is probably more readable. |
If we differentiate move-only types could we have a warning if they are used after they got moved out? |
That may be difficult because (at least for STL types), at a minimum a move leaves the object in a "valid but unspecified" state. So it's then valid to call a member function on a moved-from-object if that function has no preconditions. You can then put the object back into a well-known state. E.g. main: () = {
v1: std::vector = (11, 22, 33);
v2:= (move v1);
// This is fine; `clear` has no preconditions and
// puts the vector back into a well-known state.
v1.clear();
v1.push_back(44);
std::print("v1: {} v2: {}", v1, v2);
} (See C++ standard "[lib.types.movedfrom]".) Some types have stronger guarantees. For example, a move from |
Yes, I understand that move from objects are 'valid but unspecified' but so are uninitialized int variables but we still consider them a problem. So maybe we could treat moved from objects like uninitialized. You need to bring them into a know state and since they are still valid that is possible. |
Are there any performance issues with considering moved from objects as uninitialised? If not, it sounds like an excellent idea IMO, and I know that certain actions on moved from objects are legal, but are any of them useful?
On 22 March 2024 22:33:14 Ingo Prötel ***@***.***> wrote:
Yes, I understand that move from objects are 'valid but unspecified' but so are uninitialized int variables but we still consider them a problem. So maybe we could treat moved from objects like uninitialized. You need to bring them into a know state and since they are still valid that is possible.
In my pull request #914<#914> I tried to create an 'owning reference' that is like a unique pointer only it cannot be null. You can hand the ownership to another reference but than the initial reference must not be used. I can enforce that with a runtime check but would like to have some compiler support. If it was treated like uninitialized it could trigger an error.
—
Reply to this email directly, view it on GitHub<#1030 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQJ5SQA33VE52GWQAGLYZSWSPAVCNFSM6AAAAABE4UCVXGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMJWGAZTEMZXGA>.
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
Brief update: On reviewing these changes in a recent commit discussion, I think I'm going to switch gears and not move from last use for object whose names start with |
Background:
The current move from last-use behaviour causes problems with RAII types, and types that have non-const (
inout this
) member functions with side effects, where the programmer does not want to inspect the object's value after calling the non-const member function.For example, maybe that function notifies an observer, or calls an OS/graphics API, or a different thread will read the object's new value.
See: #999 and #1002
Suggestion:
Move from last-use seems perfectly suited to value types, as opposed to RAII, guards, managers, and non-value types that model the problem domain. So the move from last-use behaviour could be applied only to Cpp2 types which implement the
@basic_value
metafunction, as well as well-known STL types (or, if possible to be generic, types that meet some suitable compile-time criteria).Alternatives considered:
The above suggestion is effectively an "opt-in" approach for the move from last-use feature.
An alternative would be to make move from last-use "opt-out", using something like:
@RAII
,@guard
, etc inheriting from@no_move_from_last_use
)unique_lock
) which cannot be modified with Cpp2 metafunctions, prefix the variable with some attribute ([[no_move_from_last_use]]
)However, since one of Cpp2's stated goals is to have "full backward compatibility" with ISO C++, and Cppfront enables a mixed-mode for side-by-side easy migration, I'd expect to be able to use my Cpp1 types seamlessly in Cpp2 code without changing their semantics. E.g. a RAII type from C++ should work in Cpp2 like it already does, just like
std::string
works in Cpp2 like it already does in Cpp1. That's why I think opting-in to the Cpp2 move from last-use behaviour is the correct choice.The text was updated successfully, but these errors were encountered: