5

Quoted from C++ Primer

if we explicitly ask the compiler to generate a move operation by using = default, and the compiler is unable to move all the members, then the move operation will be defined as deleted

the move constructor is defined as deleted if the class has a member that defines its own copy constructor but does not also define a move constructor, or if the class has a member that doesn’t define its own copy operations and for which the compiler is unable to synthesize a move constructor

Some code seems to violate this rule:

#include <utility>
#include <iostream>

struct X {
    X() = default;
    X(const X&) { std::cout << "X(const X&)" << std::endl; }
    int i;
};

struct hasX {
    hasX() = default;
    hasX(const hasX &) = delete;
    hasX(hasX &&) = default;
    X mem;
};


int main()
{
    hasX hx, hx2 = std::move(hx);  //output is X(const X&)
}

X doesn't define move constructor and compiler can't synthesize one for it.

According to the rule above, hasX's move constructor is deleted.

However, because hasX's copy constructor is deleted, hx2 = std::move(hx) must call move constructor to output "X(const X&)", which shows hasX's move constructor is defined and it use X's copy constructor to "move". This seems to violate the rule above.

So, is't defined in C++ standard or just a compiler implementation?

Compiler I tested: VS2015 and a online compiler

Thank you for help!

Andy Guo
  • 127
  • 2
  • 7

1 Answers1

3

Your book seems to be wrong. Copying is a valid move operation so as long as the member has a copy constructor in the form of const type&, so it can bind to rvalues, the move operation will fall back to a copy.

In your example

hasX(hasX &&) = default;

Can be replaced with

hasX(hasX &&rhs) : mem(std::move(rhs.mem)) {}

as that is what the default does and it will compile just fine.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Can you give me a standard reference to support this? I'm having trouble backing up the claim. The standard appears to me to specify otherwise (N3337 12.8/11, 9/6, 12.8/12) – AndyG Jun 21 '17 at 18:22
  • @AndyG The linked to answer has all the quotes to back up my assertion. Also what about 12.8/11 makes you think it is not allowed? – NathanOliver Jun 21 '17 at 18:25
  • I find that "a non-static data member of class type M (or array thereof) that cannot be **copied/moved** because overload resolution". – Andy Guo Jun 21 '17 at 18:28
  • @NathanOliver: The part about references in the linked answer clears things up, but honestly it reads to me like there's a contradiction in the standard. 12.8/11 states that a defaulted move constructor is deleted if `X` has a non-static data member that does not have a move constructor and is not trivially copyable. Of course it doesn't have a move ctor because it has a user-provided copy ctor. And it's not trivially copyable (9/6) because it has a user-provided copy ctor (12.8/12) – AndyG Jun 21 '17 at 18:42
  • @AndyG I think your reading 12.8/11 wrong. It states *a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution[...]* It says *copied/moved*. There is no requirement that it be trivial. – NathanOliver Jun 21 '17 at 18:44
  • @AndyG Never mind, it was me. I think the line you are referencing was removed with a DR. *for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.* does not exist starting in at least N3690. – NathanOliver Jun 21 '17 at 18:50
  • Found a DR from [this answer](https://stackoverflow.com/a/20609313/4342498) – NathanOliver Jun 21 '17 at 18:53
  • @NathanOliver: You are, of course, correct. The bullet point I cited is a few down from the one you just mentioned. The two have no stated precedence, but I think that it's clear the one you cited should take precedence over the one I cited. – AndyG Jun 21 '17 at 18:53
  • @NathanOliver: Wow 2 comments in the time it took me to write that last one! So it seems that the wording was changed between N3337 and the published standard? – AndyG Jun 21 '17 at 18:55
  • 1
    @AndyG Yes. It was a defect on the standard and has been recitifed. IIRC DR's backdated to the standard they are for so it applies to the C++11 standard. – NathanOliver Jun 21 '17 at 18:56
  • @NathanOliver: Excellent research. Maybe the book that OP cites was not wrong when it was written :-) – AndyG Jun 21 '17 at 18:59
  • @AndyG Could be. Think I should change that part? – NathanOliver Jun 21 '17 at 19:00
  • @NathanOliver: Nah, your statement is technically correct. – AndyG Jun 21 '17 at 20:26