4 ms·
My function can choose to move or not to move from an object based on io input.
by adrianN 10mo ago
My function can choose to move or not to move from an object based on io input.
- tsimionescu 10mo agoCan you show an example of what you mean? My claim is that, if I call `foo(std::move(myObj))`, it is statically knowable if `foo` receives a copy of `myObj` or whether it is moved to it. Of course, `foo` can choose to further copy or move the data it receives, but it can't choose later on if it's copied or not. Now, if I give `foo` a pointer to myObj, it could of course choose to copy or move from it later and based on runtime info - but this is not the discussion we are having, and `std::move` is not involved from my side at all.
- masklinn 10mo ago> Can you show an example of what you mean? void foo(std::unique_ptr<int, Deleter>&& p) { std::random_device rdev {}; auto dist = std::uniform_int_distribution<>(0, 1); if (dist(rdev)) { auto pp = std::move(p); } }
- tsimionescu 10mo agoThis is exactly what I meant as irrelevant. If I call `foo(std::move(my_unique_ptr))`, I know for sure, statically, that my_unique_ptr was moved from, as part of the function call process, and I can no longer access it. Whether `foo` chooses to further move from it is irrelevant.
- masklinn 10mo agoThe only thing that is statically known here is that you’re wrong. The function I posted only moves its parameter half the time, at random. You may want to treat it as moved-from either way, but factually that’s just not what is happening.
- charcircuit 10mo agoThis is like trying to defend that you can't statically know the result of 1 + 2 because: void foo() { std::random_device rdev {}; auto dist = std::uniform_int_distribution<>(0, 1); if (dist(rdev)) { int res = 1 + 2; } } I can tell you for sure that the result of 1 + 2 will be 3.
- masklinn 10mo ago> This is like trying to defend that you can't statically know the result of 1 + 2 It is completely unlike that. tsimionescu is asserting that they can always know statically whether `foo` will move its parameter. The function I provided is a counter-example to that assertion. Of course the branch body always moves, that's what it's there for. That has no bearing on the argument.
- charcircuit 9mo ago>Of course the branch body always moves >That has no bearing on the argument. That is the whole argument. Let me quote the other person: "My claim is that, if I call `foo(std::move(myObj))`, it is statically knowable if `foo` receives a copy of `myObj` or whether it is moved to it." It is saying that for "auto pp = std::move(p);" we will know if it uses the move assign constructor or the copy assign constructor.
- masklinn 9mo ago> That is the whole argument No, it is not. > Let me quote the other person: "My claim is that, if I call `foo(std::move(myObj))`, it is statically knowable if `foo` receives a copy of `myObj` or whether it is moved to it." Yes. `foo`. > It is saying that for "auto pp = std::move(p);" we will know if it uses the move assign constructor or the copy assign constructor. `pp` is not `foo`. That `pp` uses a move constructor is not the subject of the debate. You can literally take the function I posted, build a bit of scaffolding around it, and observe that whether the parameter is moved into `foo` or not is runtime behaviour: https://godbolt.org/z/jrPKhP35s https://godbolt.org/z/jrPKhP35s
- knorker 10mo agoNo: https://godbolt.org/z/d7f6MWcb5 https://godbolt.org/z/d7f6MWcb5 Look, the act of calling std::move and and calling a function taking an rvalue reference in no way invokes a move constructor or move assignment. It does not "move". It's still just a reference, albeit an rvalue reference. std::move and the function shape is about the type system, not moving. (Edit: amusingly, inside the callee it's an lvalue reference, even though the function signature is that it can only take rvalue references. Which is why you need std::move again to turn the lvalue into rvalue if you want to give it to another function taking rvalue reference) I didn't reply to this thread until now because I thought you may simply be disagreeing about what "move" means (I would say move constructor or move assignment called), but the comment I replied to makes a more straightforward factually incorrect claim, that can easily be shown in godbolt. If you mean something else, please sketch something up in godbolt to illustrate your point. But it does sound like you're confusing "moving" with rvalue references. Edit: for the move to happen, you have to actually move. E.g. https://godbolt.org/z/b8M495Exq https://godbolt.org/z/b8M495Exq
- tsimionescu 9mo agoThanks for the godbolt link, it really helped me understand where my mistake was. I was treating r-value references in my mind as if they are "consuming" the value, but of course, as reference types, they are not.
- tialaramex 10mo agoYeah no. In Rust if you pass say a Box<Goose> (not a reference, the actual object) into a function foo, it's gone, function foo might do something with that boxed goose or it might not, but it's gone anyway. If a Rust function foo wanted to give you it back they'd have to return the Box<Goose> But C++ doesn't work that way, after calling foo my_unique_ptr is guaranteed to still exist, although for an actual unique_ptr it'll now be "disengaged" if foo moved from it. It has to still exist because C++ 98 (when C++ didn't have move semantics) says my_unique_ptr always gets destroyed at the end of its scope, so newer C++ versions also destroy my_unique_ptr for consistency, and so it must still exist or that can't work. Creating that "hollowed out" state during a "move" operation is one of the many small leaks that cost C++ performance compared to Rust.
- knorker 9mo agoYou should not be downvoted, which you appear to be. Your comparison is both correct and interesting. Maybe you're being too verbose for your point, and it would help readers if you summarize and narrow the argument to: In Rust a function signature can force a move to happen at call time (by being non-reference and not Copy), but in C++ a function taking rvalue reference (&&) only signals the callee that it's safe to move if you want, as it's not an lvalue in the caller. It's an added bonus that Rust prevents reusing the named variable in the caller after the move-call, but it's not what people seem to be confused about.
- vlovich123 10mo agoaside from what others wrote, it’s also non local - whether std::move even does anything is dependent on the signature of foo - if foo takes it by const& you may think you’ve transferred ownership when it hasn’t actually happened.
- masklinn 9mo agoThat is static though, that `foo` takes its parameter by `const&` and will thus not move it is available to the compiler (or other tooling) at compile time. The point of contention is whether that is always the case, or whether there are situations where moving from the parameter is a runtime decision.
- vlovich123 9mo agoOthers have already answered that for you. I specifically said it’s non local which means it’s difficult for a human to reason about at the call site.
- ben-schaaf 10mo agoNo, it is not statically knowable if it is actually moved. void foo(Obj && arg) {} Does not move `arg`. It's fairly easy to write code that assumes `std::move` moves the value, but that can lead to bugs. For example: void some_function(std::vector<int> &&); void some_function2(std::vector<int> &&); void main() { std::vector<int> a = { 1 }; some_function(std::move(a)); a.push_back(2); some_other_function(std::move(a)); } The expectation is that `some_other_function` is always called with `{ 2 }`, but this will only happen if `some_function` actually moves `a`.
- adrianN 9mo agoIs pushing to a moved-from vector even legal? I thought in general the only guarantee you have after a move is that is save to destruct the object.
- ben-schaaf 9mo agoThe state of a moved-from value is valid but unspecified (note, not undefined). IIRC the spec says vector must be `empty()` after a move. So all implementations do the obvious thing and revert back to an empty vector.
- tsimionescu 9mo agoYou're right, of course - I was completely messing up in my mind what r-value reference parameters actually do, and thinking that they need to be moved to, when the whole point is that they don't, they're just a reference.