Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this call the copy constructor, not the move constructor?

I have a class, PlayerInputComponent:

.h:

class PlayerInputComponent
{
public:
    PlayerInputComponent(PlayerMoveComponent& parentMoveComponent_, std::unique_ptr<IRawInputConverter> inputConverter_);
    PlayerInputComponent(PlayerInputComponent&& moveFrom);
    void update();

private:
    std::unique_ptr<IRawInputConverter> inputConverter;
    PlayerMoveComponent& parentMoveComponent;
};
}

.cpp:

PlayerInputComponent::PlayerInputComponent(PlayerMoveComponent& parentMoveComponent_, std::unique_ptr<IRawInputConverter> inputConverter_) :
    parentMoveComponent(parentMoveComponent_),
    inputConverter(std::move(inputConverter_))
{
}

PlayerInputComponent::PlayerInputComponent(PlayerInputComponent&& moveFrom) :
    parentMoveComponent(moveFrom.parentMoveComponent),
    inputConverter(moveFrom.inputConverter.release())
{
}

and a class, PlayerMoveComponent, that contains a PlayerInputComponent member and initializes it using a std::unique_ptr passed as a parameter. Its constructor:

PlayerMoveComponent::PlayerMoveComponent(/* other parameters */ std::unique_ptr<IRawInputConverter> inputConverter) :
    //other initializations
    inputComponent(PlayerInputComponent(*this, std::move(inputConverter)))
{
}

I defined my own move constructor for the PlayerInputComponent class since my understanding is that a default move constructor won't be constructed for a class which contains a reference member. In this case though I know that the reference will remain in scope for duration of the PlayerInputComponent object's lifetime.

Since I'm initializing the PlayerMoveComponent's inputComponent variable from a temporary, I believe one of the following two things is supposed to happen:

  1. PlayerInputComponent's move constructor is used to initialize the playerInputComponent member variable.
  2. The move is elided by the compiler.

However, Visual Studio 2012 spits this out:

error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
1>          with
1>          [
1>              _Ty=SDLGame::IRawInputConverter
1>          ]
1>          c:\program files\microsoft visual studio 11.0\vc\include\memory(1447) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr'
1>          with
1>          [
1>              _Ty=SDLGame::IRawInputConverter
1>          ]
1>          This diagnostic occurred in the compiler generated function 'PlayerInputComponent::PlayerInputComponent(const PlayerInputComponent &)'

Why is the copy constructor being called here? Making the PlayerInputComponent class's parentMoveComponent member a regular ParentMoveComponent instance, rather than a reference, gets rid of the error, but I don't understand why - I've tested and verified that move constructing objects with reference members works so long as you provide your own move constructor, so what's the deal?

like image 685
Benjamin Good Avatar asked Apr 30 '13 23:04

Benjamin Good


People also ask

What is the difference between a move constructor and a copy constructor?

Move constructor moves the resources in the heap, i.e., unlike copy constructors which copy the data of the existing object and assigning it to the new object move constructor just makes the pointer of the declared object to point to the data of temporary object and nulls out the pointer of the temporary objects.

Why is my copy constructor not being called?

The reason the copy constructor is not called is because the copy constructor itself is a function with one parameter. You didn't call such function,so it didn't execute.

Why is copy constructor called reference?

When we create our own copy constructor, we pass an object by reference and we generally pass it as a const reference. One reason for passing const reference is, we should use const in C++ wherever possible so that objects are not accidentally modified.

Can I call constructor from copy constructor?

The answer is No. The creation of the object memory is done via the new instruction. Copy constructor is then in charge of the actual copying (relevant only when it's not a shallow copy, obviously). You can, if you want, explicitly call a different constructor prior to the copy constructor execution.


2 Answers

If you initialize a new Object using =, the copy constructor will be triggered by default. To trigger the move constructor, you need to alter the behavior of operator= You can find an example here Hope I helped you.

like image 50
Kostas Andrianos Avatar answered Oct 18 '22 03:10

Kostas Andrianos


I'm sorry in advance if this doesn't really answer your question, I just want to react on the apparent complexity of your problem. If I may, wouldn't this be a thousand times simpler:

/********************     **********     ********************/

class C {};
class B;



class A
{
public:

    A(): _b(nullptr), _c(nullptr) {}
    A( B *b, C *c ): _b(b), _c(c) {}
    A( A&& a ): _b(a._b), _c(a._c) {}

private:

    C *_c;
    B *_b;
};



class B
{
public:

    B( /* other parameters */ C *c ): _a( A(this,c) ) {}

private:

    A _a;
};


    /********************     **********     ********************/


int main()
{
    C c;
    B b(&c);
}

and yet achieve the same thing? I have nothing against using the new features in c++11, like std::unique_ptr, but IMHO, ensuring that a pointer can never be dereferenced from two places should not be a matter of run-time checking (except maybe in very rare cases), but a matter of design.. shouldn't it?

like image 34
Jonathan H Avatar answered Oct 18 '22 03:10

Jonathan H