19

I just spent an inordinate amount of time fiddling with a complilation error in Visual Studio. I have distilled the code into the small compilable example below and tried it on IdeOne and got the same error which you can see here.

I am wondering why the following code tries to call B(const B&) instead of B(B&&):

#include <iostream>

using namespace std;

class A {
public:
    A() : data(53) { }
    A(A&& dying) : data(dying.data) { dying.data = 0; }

    int data;

private:
    // not implemented, this is a noncopyable class
    A(const A&);
    A& operator=(const A&);
};

class B : public A { };

int main() {
    B binst;

    char* buf = new char[sizeof(B)];

    B* bptr = new (buf) B(std::move(binst));

    cout << bptr->data << endl;

    delete[] buf;
}

I didn't explicitly define any constructors, so B(std::move(binst)) should call the compiler generated B(B&&), no?

When I change B to

class B : public A {
public:
    B() { }
    B(B&&) { }
};

It compiles fine. Why is this?

It will be extremely inconvenient if this can't be fixed from the base class because I have a template class which uses placement new and move constructors like the example, and it will require every class that is not copyable (which is not and definitely should not be a requirement for use with my template class) to have an explicitly defined move constructor.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • Is there a compiler generated B(B&&)? My C++11 is still a bit rusty. And would your B(B&&) need to initialise A(A&&)? – CashCow Jan 24 '12 at 18:21
  • @CashCow yes, all it does is move each member (if that's wrong then I'm looking at you, Kerrek SB `:)`) – Seth Carnegie Jan 24 '12 at 18:22
  • Compiles fine on GCC 4.6, GCC 4.7, and Clang 3.0. – R. Martinho Fernandes Jan 24 '12 at 18:22
  • 2
    @CashCow: There should be, but VS2010 doesn't make one. – Nicol Bolas Jan 24 '12 at 18:22
  • 3
    Works fine on GCC 4.6.2. – Kerrek SB Jan 24 '12 at 18:22
  • Under very strict circumstances which appear to be met here (see http://stackoverflow.com/questions/4819936/why-no-default-move-assignment-move-constructor) a default move constructor will be generated. It seems likely that this is something that changed in the standard and g++ 4.7 has a far better shot at having implemented. – Mark B Jan 24 '12 at 18:23
  • Doesn't work on [gcc-4.5](http://ideone.com/8zo1D), but it works on gcc-4.6 and 4.7. – kennytm Jan 24 '12 at 18:24
  • You did define a move constructor in `A` though. So `B` is not a trivial type anymore. – wilhelmtell Jan 24 '12 at 18:25
  • By the way, in `A(A&&)` you should use `std::move()` for initialization. – wilhelmtell Jan 24 '12 at 18:25
  • Did you try to move the object into a normal constructed object, i.e. simething like: `B b(std::move(binst));`? The code look OK, I think, although the generated constructors may depend on the subobject's constructors being accessible: the generated move constructor is easily prevented to avoid breaking existing code. The private copy constructor in the base may put it off. – Dietmar Kühl Jan 24 '12 at 18:26

2 Answers2

17

If you are using Visual Studio 2010 or 2012, be advised: the compiler does not automatically generate move constructors for you. That wasn't implemented. So you need to write them yourself.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 2
    It should be noted that the details of when a move constructor should be implicitly-defined as defaulted have changed near the end of the standardization process and vc2010 has been around before that. Also, vc2010 does not support `= delete`, which makes it difficult to talk about conformance of move constructors :) – avakar Jan 24 '12 at 18:26
  • That is seriously annoying. I long for VS11. Thanks. – Seth Carnegie Jan 24 '12 at 18:29
  • 4
    @Seth Carnegie: VC11 will not generate automatically move constructor either... http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx "Rvalue references v3.0 adds new rules to automatically generate move constructors and move assignment operators under certain conditions. This will not be implemented in VC11, which will continue to follow VC10's behavior of never automatically generating move constructors/move assignment operators." – Arzar Jan 25 '12 at 07:24
6

You must be facing a compiler bug. The standard says that B gets an implicitly declared and defined move constructor; all the conditions of 12.8(9) are met (i.e. B does not have an explicitly declared copy constructor, copy-assignment, etc, and the move constructor would not implicitly be declared deleted).

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084