2

MWE:

struct A {
    A() {std::cout << "constructor" << std::endl; }
    A(const A& a) {std::cout << "copy constructor" << std::endl; }
    A(A&& a) {std::cout << "move constructor" << std::endl; }
};

int main() {
    A a1{};
    A a2{ a1 };
    A a3{ A{} };
    A a4{ std::move(a3) };
    return 0;
}

Output:

constructor
copy constructor
constructor
move constructor

fora2 copy elision is used which is an optimization of the compiler and everything seems to be fine. When I comment out the move constructor however, copy constructor is called in place of move constructor. How can an rvalue be converted to a const lvalue reference? Output:

constructor
copy constructor
constructor
copy constructor

The program is compiled in VS2017.

rowman
  • 1,516
  • 1
  • 16
  • 26
  • 3
    *"How can an rvalue be converted to a const lvalue reference?"* Easily. Happens all the time. Why do you think this would be a problem? Well, "converted" is perhaps a wrong term: a const lvalue reference can be *bound* to a temporary object. – Igor Tandetnik Jul 04 '18 at 05:17
  • @IgorTandetnik, Yes you're right. It all happens due to `const`, didn't know this before. Maybe you can elaborate abit about the `bounding` as you mentioned in an answer. How does it work? – rowman Jul 04 '18 at 05:25
  • *"How can an rvalue be converted to a const lvalue reference?"* isn't this the basics of pass-by-copy optimization ? – Jean-Baptiste Yunès Jul 04 '18 at 05:25
  • @Jean-BaptisteYunès What is "pass-by-copy optimization"? – curiousguy Jul 04 '18 at 05:27
  • 1
    @curiousguy replacing `f(T v)` by `f(const T &v)` to eliminate the copy at each call. – Jean-Baptiste Yunès Jul 04 '18 at 05:29
  • @Jean-BaptisteYunès It isn't just an "optimization". It's inherently part of the concept of a copy constructor! If you don't have reference, good luck trying to define copy operations. – curiousguy Jul 04 '18 at 05:32
  • @curiousguy many languages don"t have such, pointers and functions are sufficient. – Jean-Baptiste Yunès Jul 04 '18 at 13:37
  • @Jean-BaptisteYunès They probably don't have operator overloading, user defined types with syntax similar to builtin types, flat structures (user defined types don't create indirections)... – curiousguy Jul 04 '18 at 22:02

1 Answers1

2

From en.cppreference.com:

If both copy and move constructors are provided and no other constructors are viable, overload resolution selects the move constructor if the argument is an rvalue of the same type (an xvalue such as the result of std::move or a prvalue such as a nameless temporary (until C++17)), and selects the copy constructor if the argument is an lvalue (named object or a function/operator returning lvalue reference). If only the copy constructor is provided, all argument categories select it (as long as it takes a reference to const, since rvalues can bind to const references), which makes copying the fallback for moving, when moving is unavailable.

This clearly says that rvalue can be bound to const lvalue reference.

code707
  • 1,663
  • 1
  • 8
  • 20
  • Copy is the fallback. What about if copy & move are both available? Is move only used (starting 17) with explicit moves? I can confirm that nameless temporaries are handled through something else, though I can't generate a print message to tell me precisely what. – Victor Eijkhout Mar 08 '21 at 16:51