0

Consider this example:

#include <vector>
void addRow(std::vector<std::vector<double>>& matrix, const std::vector<double>& row) {
    matrix.push_back(std::move(row));
}

I would've expected issues with std::move and a const & as std::move basically invalidates that reference since it's contents are moved and it is set to a clean state, and this is the exact behavior const must prevent.

This was compiled using: g++ -std=c++20 -Ofast *.cpp -o test with no warnings or errors and runs fine.

I've looked at other posts: Move construction from const reference -> apparently this didn't use to work

Const reference VS move semantics -> no answer

Why can we use `std::move` on a `const` object? -> apparently it works on a const as well, though that's not concerning

What's concerning about the reference, is it's invalidating an object it's promising not to modify.

The mostly frustrating thing about my post being labeled a duplicate is that I linked the 'duplicate' and explicitly stated it didn't answer my question, also expressing the reason why. Thanks.

  • 3
    It doesn't invalidate anything, it silently falls back to copying (unless the class has an unusual move constructor accepting `const T &&`). – HolyBlackCat Dec 29 '22 at 09:29
  • Do you have a source for this @HolyBlackCat ? I was expecting some sort of compilation error, which I would've preferred to silent behavior. – user8393252 Dec 29 '22 at 09:31
  • https://en.cppreference.com/w/cpp/utility/move It doesn't remove constness. – tkausl Dec 29 '22 at 09:32
  • 1
    You can test it yourself on a custom class that logs the constructor calls. It natually follows from `std::move` not removing constness (just adding `&&`). Then, because `const T &&` isn't convertible to `T &&` (that would drop constness) but is convertible to `const T &`, the latter ends up being used. – HolyBlackCat Dec 29 '22 at 09:33
  • @HolyBlackCat So if I'm following you, it's cast to an rvalue reference (reference to temp), and the `const` takes precedence so it is converted to a `const` lvalue reference (reference to object with duration). What exactly does this change? – user8393252 Dec 29 '22 at 09:37
  • 2
    What I'm saying is, you have a copy and a move constructor: `T(const T &)` and `T(T &&)`. The second one can't be used since there's no conversion from `const T &&` to `T &&`. So the first one is used. – HolyBlackCat Dec 29 '22 at 09:39
  • I see. Why exactly is there an rvalue reference involved for a move constructor? Intuitively, I'd think it'd be a regular lvalue reference. – user8393252 Dec 29 '22 at 09:45
  • If you run clang-tidy, it will notify you that moving const reference does nothing but copying. – Louis Go Dec 29 '22 at 09:50
  • `std::move()` cast lvalue to rvalue then `T(T &&)` would work. – Louis Go Dec 29 '22 at 09:52
  • What is the purpose of the rvalue here @LouisGo ? I don't ever really understand r value references – user8393252 Dec 29 '22 at 10:06
  • Then you need to maybe try to research rvalue references. – Öö Tiib Dec 29 '22 at 10:09
  • Its purpose is to allow moves. You can't move from lvalues, by definition. – tkausl Dec 29 '22 at 10:10
  • I believe you could move a non-const lvalue @tkausl . In my program, I went through (removing the `const` in the function in the post), and it seems to move it correctly. – user8393252 Dec 29 '22 at 10:21
  • `I went through, and it seems to move it correctly.` Of course it does, because you pass it through `std::move`. If you didn't, it wouldn't move, because it doesn't move a lvalue. – tkausl Dec 29 '22 at 10:24
  • Are you saying that an rvalue reference to `my_vector.pushback(a)` where `a` is the rvalue reference, would be moved? – user8393252 Dec 29 '22 at 10:25
  • No, you'd still need a `std::move`. – tkausl Dec 29 '22 at 10:36
  • I believe this may be out of my scope, since I'm not quite understanding rvalue references @tkausl and you aren't really explaining them. – user8393252 Dec 29 '22 at 10:47
  • `std::move` does **not** move anything, it was just a lot shorter than `std::permission_granted_to_suck_out_the_objects_innards_and_leave_behind_an_empty_husk`. It treats the named object as an rvalue. If the object was a `const` lvalue, it'll be treated as a `const` rvalue. Still `const`. – Eljay Dec 29 '22 at 13:35
  • What does treating it as an rvalue do @Eljay ? I don't really get that. I've read about 'perfect forwarding' and understand it may be related to that. – user8393252 Dec 29 '22 at 20:27
  • Treating the named variable as an rvalue gives permission to steal all of the object's internal state, leaving it in a valid-but-unspecified state (suitable for re-assignment or destruction). – Eljay Dec 29 '22 at 22:41
  • But couldn't you also do that to an lvalue? @Eljay – user8393252 Dec 29 '22 at 23:13
  • That's **how** you do that to an lvalue. – Eljay Dec 29 '22 at 23:56
  • Why is it not permitted to just do it to the lvalue itself @Eljay? – user8393252 Dec 30 '22 at 02:45
  • For example, if I were to perform `std::move` on a `std:vector&` that is not `const`? @Eljay – user8393252 Dec 30 '22 at 04:45
  • @HolyBlackCat what would happen if the class accepted `const &&`? I'm really trying to understand this. – user8393252 Dec 30 '22 at 04:46
  • @user8393252 Then that constructor would be preferred over any other, but it's unclear what you would do in it, since moving normally modifies the source object. – HolyBlackCat Dec 30 '22 at 05:28
  • @HolyBlackCat why is `std::move` not able to modify the source if it's just a regular reference `&`. I don't really see the purpose of an rvalue, nor do I understand what it really is. – user8393252 Dec 30 '22 at 06:14
  • An **rvalue** is an object that is about to become unused, as such the compiler can optimize the operation to just *move* the internal state of the about-to-go-away value into the the other object. If that **rvalue** is `const`, then the compiler is **not** allowed to *move* the internal state to the other object, and instead has to *copy* the internal state. – Eljay Dec 30 '22 at 12:37
  • So if it's an _lvalue_, it cannot move the internal state @Eljay ? Someone else told me that you could, and `std::move` could take a `&` parameter, but `&&` makes it more versatile. – user8393252 Dec 30 '22 at 20:50
  • You should ask that *someone* what they're talking about. – Eljay Dec 30 '22 at 21:32
  • So you're saying that you could not steal the contents of a `&` and a `&&` makes it possible? It was in a thread in another question I asked: https://stackoverflow.com/questions/74958711/what-is-the-purpose-of-an-rvalue-reference-for-stdmove – user8393252 Dec 30 '22 at 21:37
  • Here is a direct quote (they received 2 upvotes) : "In the case of std::move they could have used & instead of &&, but then std::move would fail to compile on a rvalue, which might be annoying in generic code. It is not useful to intentionally pass a rvalue to std::move. But a forwarding reference accepts both lvalues and rvalues. " – user8393252 Dec 30 '22 at 21:38
  • You are conflating *forwarding references* with *rvalue references*. Those are two different things (albeit with similar syntax), used in different contexts. – Eljay Dec 30 '22 at 23:29

0 Answers0