【问题标题】:Why does this call the copy constructor, not the move constructor?为什么这调用复制构造函数,而不是移动构造函数?
【发布时间】:2013-04-25 00:09:04
【问题描述】:

我有课,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())
{
}

还有一个类PlayerMoveComponent,它包含一个PlayerInputComponent 成员并使用作为参数传递的std::unique_ptr 对其进行初始化。它的构造函数:

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

我为PlayerInputComponent 类定义了自己的移动构造函数,因为我的理解是不会为包含引用成员的类构造默认移动构造函数。在这种情况下,尽管我知道引用将在 PlayerInputComponent 对象的生命周期内保持在范围内。

由于我正在从临时初始化PlayerMoveComponentinputComponent 变量,我相信应该会发生以下两件事之一:

  1. PlayerInputComponent的移动构造函数用于初始化playerInputComponent成员变量。
  2. 编译器忽略了移动。

然而,Visual Studio 2012 吐出这一点:

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 &)'

为什么在这里调用复制构造函数?使 PlayerInputComponent 类的 parentMoveComponent 成员成为常规 ParentMoveComponent 实例,而不是引用,可以消除错误,但我不明白为什么 - 我已经测试并验证了使用引用成员构造对象的移动工作只要您提供自己的移动构造函数,那有什么关系?

【问题讨论】:

  • @MooingDuck:我不明白这如何回答这个问题。我很确定这是 MSVC 中的一个错误。
  • @JesseGood: oh you're right,我误读了部分代码。然后是This bug report of mine:P
  • 上述类的声明,被精简为只包含可重现的结果(这似乎不太难)也很高兴看到,特别是@ 987654340@
  • @yngum:假设inputComponentPlayerInputComponent,然后你告诉它从参数构造PlayerInputComponent,然后从那个临时移动构造inputComponent。大概你的意思是inputComponent(*this, std::move(inputConverter))
  • @JonathanWakely,是的,它可以从右值移动,但在不知道 inputComponent 实际上是什么类型的情况下,我不知道右值可用于在不调用副本的情况下对其进行初始化.特别是,如果inputComponent 是一个包含可复制但不可移动成员的类,它也将(默认情况下)可复制但不可移动,这意味着移动构造函数实际上是一个复制构造函数。根据inputComponent 的类型提供的构造函数,可能会出现类似的副作用。这就是为什么我认为我们需要知道它的类型,而我在 OP 中看不到它。

标签: c++ c++11 move-semantics


【解决方案1】:

如果这不能真正回答您的问题,我很抱歉,我只是想对您的问题的明显复杂性做出反应。如果可以的话,这不是简单一千倍吗:

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

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);
}

还实现了同样的目标?我不反对使用 c++11 中的新功能,例如std::unique_ptr,但是恕我直言,确保永远不能从两个地方取消引用指针不应该是运行时检查的问题(可能在极少数情况下除外),但设计问题..不应该吗?

【讨论】:

  • 我真的同意你的观点,但我认为不使用原始指针的主要原因是对谁在使用它有一定的安全性。例如,在您的代码中,我可以将一个 c 指针传递给您的 B 类,然后在此调用之后删除它! (BOOM ...它会在稍后的某个时间崩溃)。如前所述,使用 unique_ptr 可以防止这种情况发生,因为控件将被传递给新的 B 实例,并且它之外的任何人都无法删除它。
  • Ahn ...顺便说一句,我不知道unique_ptr在取消引用时是否进行任何检查...如果您创建一个全新的(nullptr)并尝试访问它,它会像任何指针一样崩溃。当您想确定谁是所有者并且它必须是一个所有者(唯一)时,它很有用。
  • @JulioRaffaine 非常感谢这些 cmets! :) 我的意思是取消引用是这样的;您想要unique_ptr 的唯一原因是您不希望其他人访问它。 AFA 删除是很有趣的,我认为如果你打算使用 unique_ptr 并且你最终不使用它应该更简单:这里唯一包含唯一 c 指针的类是 A,所以逻辑上 A 应该删除在其析构函数中为 c 分配的内存,并在它被简单复制时“取消”它。
【解决方案2】:

如果你使用=初始化一个新的对象,复制构造函数会默认被触发。 要触发移动构造函数,您需要更改operator= 的行为 你可以找到一个例子here 希望我对你有所帮助。

【讨论】:

    猜你喜欢
    • 2012-04-29
    • 2015-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-21
    • 2019-11-07
    相关资源
    最近更新 更多