4

I have the following piece of code:

#include <iostream>

struct T {
    int a;

    T() = default;

    T(T& other) {
        std::cout << "copy &\n";
    }

    T(T&& other) {
        std::cout << "move &&\n";
    }
};

void foo(T&& x) {
    T y(x); // why is copy ctor called??????
}

int main() {
    T x;
    foo(std::move(x));

    return 0;
}

I don't understand why copy constructor is preferred over move constructor even though foo() accepts rvalue-reference.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
mouse_00
  • 593
  • 3
  • 13

1 Answers1

7

x is an lvalue itself, even its type is rvalue-reference. Value category and type are two independent properties.

Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;

You need to use std::move to convert it to rvalue, just same as using std::move on x in main().

void foo(T&& x)
{
    T y(std::move(x));
}
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • For a moment I was going to say `std::forward` would be more appropriate here, then realized the OP had named a `struct` `T` (concrete type, so `T&&` is actually a r-value reference), it wasn't the typical `template` `T` case (where `T&&` would be a universal reference). Sigh... – ShadowRanger Nov 25 '21 at 01:08
  • No, it would not, if T is a parameter of a struct template. – 273K Nov 25 '21 at 01:17
  • I wouldn't have guessed this in 1000 years. With all the quirks of C++ you can never get bored! – Fabio says Reinstate Monica Nov 25 '21 at 01:25