1

I'm on the last appendix of C++ primer 5th edition. (Solution part):

Here is an example from there:

Assume Y is a class that defines its own copy-constructor but not a move-constructor

 struct hasY{
     hasY() = default;
     hasY(hasY&&) = default;
     Y mem; // hasY will have a deleted move constructor
 };
 hasY hy, hy2 = std::move(hy); // error: move constructor is deleted

I've added the definition of struct Y:

struct Y{
    Y() = default;
    Y(Y const&){cout << "Y's cpy-ctor\n";}
};
  • When I run the program, it works just fine and doesn't complain about the hasY's deleted move constructor. And I get the output:

     Y's cpy-ctor
    
  • So I think this works because objects of type Y are moved through the copy-constructor but not the reverse. So calling the hasY's move-ctor invokes Y's move-ctor which is deleted then the compiler uses Y's copy-ctor to move that element. Am I right? guide me. Thank you!

Itachi Uchiwa
  • 3,044
  • 12
  • 26

1 Answers1

3

I've added the definition of struct Y

It is what the book mentions, but not the sort of type that will trigger that behavior. Your Y does not have a deleted move constructor. Once amended:

struct Y{
    Y() = default;
    Y(Y const&){cout << "Y's cpy-ctor\n";}
    Y(Y &&) = delete;
};

You'll see the behavior the book describes.

There's a big difference between having a deleted move constructor and a suppressed one. The Y you created had a user-defined copy constructor, so the move operations were suppressed. As such they did not participate in overload resolution when "moving", and the copy c'tor was chosen as fallback.

A deleted move constructor (any deleted function for that matter) participates in overload resolution. It can be selected as the best candidate, and once it is we have a program that is referring to a deleted function and becomes ill-formed.

This is a fine point that many folks stumble upon. The rule of thumb here is that the "fallback" behavior is only applicable for types that don't touch the move operations (recursively applied to bases and members). Once a move operation is declared (again, even recursively), the behavior will adjust accordingly, since we now opted in.

I recommend reading this answer by Howard Hinnant. He is one of the people directly responsible for bringing move semantics to C++11. You'll notice he suggests to never delete the move operations. It can lead to frustrating behavior being exhibited by the program. The primer's authors likely expected that behavior due to a pre-standard iteration of how move semantics were meant to manifest.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458