Live data from Hacker News

C++ std::move doesn't move anything: A deep dive into Value Categories

0xghost.dev

41–50 of 220 posts

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#41

Earlier quoted context omitted.

> Can you show an example of what you mean? void foo(std::unique_ptr && p) { std::random_device rdev {}; auto dist = std::uniform_int_distribution (0, 1); if (dist(rdev)) { auto pp = std::move(p); } }

This 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.

The 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.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#42

Earlier quoted context omitted.

Std move doesn’t move ownership. It simply casts into something that could have its ownership taken. Whether or not that actually happens is impossible to identify statically and the value after ownership is consumed is unspecified - sometimes it’s UB to access the value again, sometimes it’s not.

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".

I don't understand the downvoted here. Either the compiler emits the code to call a move constructor or it doesn't.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#43

Earlier quoted context omitted.

This 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.

The 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.

This 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.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#44

Earlier quoted context omitted.

That has about the same issue: like std::move it doesn't really explain that the receiver decides.

std::offer

∆ That's actually quite accurate. I like it!

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#46
post #22

> So the standard library plays it safe: if your move constructor might throw (because you didn’t mark it noexcept), containers just copy everything instead. That “optimization” you thought you were getting? It’s not happening. This is a bit of a footgun and clang-tidy has a check for it: performance-noexcept-move-constructor. However, I don't think it's enabled by default!

The reason performance-noexcept-move-constructor is not enabled by default is likely because blindly applying noexcept is dangerous if the underlying logic isn't actually exception-free. If you let clang-tidy slap noexcept on a move constructor that does end up throwing (perhaps because it calls into a legacy member or allocates memory internally), the runtime behavior changes from caught exception to std::terminate(…

Exceptions should never be enabled by default. We live in a 64bit world so allocations failing indicates some other problem.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#47
Sounds more like a contract thing. Of course std::move should be able to throw exceptions (like when it runs out of memory), but when it throws an exception it should still guarantee that memory is in a consistent state.

So the fault here is with std::vector who didn't write that contract.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#48
post #40

> This code works. It compiles. It runs. But depending on how you’ve implemented your types, it might be performing thousands of expensive copy operations instead of cheap moves without you realizing it. I've spent the last two decades in the .net platform. But for a decade or so before that I was a C++/Unix dev. I remember old style "C with classes" C++ as being fairly small and elegant, and approximately as easy to…

> So, what are the forces that have determined the current state of C++? A subset of the language aimed at library writers. As a user of those libraries all these weirdo features are likely to be transparent.

TFA explains how std::move is tricky to use and this is not a feature reserved for library writers

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#49
post #48
post #40

Earlier quoted context omitted.

> So, what are the forces that have determined the current state of C++? A subset of the language aimed at library writers. As a user of those libraries all these weirdo features are likely to be transparent.

TFA explains how std::move is tricky to use and this is not a feature reserved for library writers

Of course it is not reserved for library writers - nothing is. But it is not a feature that application writers should worry about overmuch.

Re: C++ std::move doesn't move anything: A deep dive into Value Categories

#50
post #22

> So the standard library plays it safe: if your move constructor might throw (because you didn’t mark it noexcept), containers just copy everything instead. That “optimization” you thought you were getting? It’s not happening. This is a bit of a footgun and clang-tidy has a check for it: performance-noexcept-move-constructor. However, I don't think it's enabled by default!

The reason performance-noexcept-move-constructor is not enabled by default is likely because blindly applying noexcept is dangerous if the underlying logic isn't actually exception-free. If you let clang-tidy slap noexcept on a move constructor that does end up throwing (perhaps because it calls into a legacy member or allocates memory internally), the runtime behavior changes from caught exception to std::terminate(…

clang-tidy checks but doesn't change things for you.

Since you can also put noexcept(false) to indicate something throws exceptions and you didn't just forget to mark it noexcept, it's not a bad policy to say every move constructor should have a noexcept marker.

Post reply on HN