5 ms·
It is absolutely knowable statically if ownership will be taken. It's not necessarily very easy to do so, but the decision is 100% up to the compiler, as part o
by tsimionescu 10mo ago
It is absolutely knowable statically if ownership will be taken. It's not necessarily very easy to do so, but the decision is 100% up to the compiler, as part of overload resolution and optimization choices (like the NRVO analysis that the article mentions). Since ownership is an inherently static concept, it doesn't even make sense to think about "runtime ownership".
- adrianN 10mo agoMy 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 10mo 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 10mo 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
- 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 10mo 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 10mo 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 10mo 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.
- charcircuit 10mo agoI don't understand the downvoted here. Either the compiler emits the code to call a move constructor or it doesn't.
- Maxatar 10mo agoStatic analysis is about proving whether the code emitted by a compiler is actually called at runtime. It's not simply about the presence of that code. Code can be emitted but never executed.
- charcircuit 10mo ago>Static analysis is about proving whether the code emitted by a compiler is actually called at runtime. That is but one thing that can static analysis can prove. It can also prove whether source code will call a move contractor or a copy constructor. Static analysis is about analyzing a program without actually running it. Analysizing what code is emitted is one way a program can be analyzed.
- knorker 10mo agoThe call to a move cons/move assign does not happen at call time. When a function taking rvalue reference is called, it can still have two code paths, one that copies the argument, and one that moves it. All the && does is prevent lvalues from being passed as arguments. It's still just a reference, not a move. Indeed, in the callee it's an lvalue reference. But yeah, you can statically check if there exists a code path that calls the copy cons/copy assign. But you'll need to check if the callee calls ANY type's copy cons/assign, because it may not be the same type as the passed in obj. At that point, what even is a move? char*p = smartptr.release() in the callee is a valid move into a raw pointer, satisfying the interface in callee. That's a move.[1] how could you detect that? [1] if this definition of move offends you, then instead remember that shared_ptr has a constructor that takes an rvalue unique_ptr. The move only happens inside the move constructor. How do you detect all cases of e.g. return cons(ptr.release()) ? It may even be the same binary code as return cons(std::move(ptr)) Probably in the end shared pointer constructor probably calls .release() on the unique ptr. That's the move. Yup. That's what https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr.html https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_p... says. (Sorry, on phone so not full code. Hopefully I stopped autocorrect all times it interfered)