这是类X
的移动构造函数
X::X(X&& rhs)
: base1(std::move(rhs))
, base2(std::move(rhs))
, mbr1(std::move(rhs.mbr1))
, mbr2(std::move(rhs.mbr2))
{ }
以下是我要注意的事情:
,
rhs
迁移了两次,不能保证rhs
处于有效状态。对于将rhs
的相应成员移动到mbr1
和mbr2
的base2
rhs
已经从(同样,不能保证它处于有效状态)中移动,为什么要这样做呢?这不是我的代码。我在一个网站上找到的。这个移动构造函数安全吗?如果是这样,又是如何做到的呢?
发布于 2014-01-25 10:09:50
这大致就是隐式move构造函数通常的工作方式:每个基对象和成员子对象都是从rhs
的相应子对象移动构造的。
假设base1
和base2
是X
的基础,没有带X
/ X&
/ X&&
/ const X&
的构造函数,那么它在编写时是安全的。当传递给基类初始化器时,std::move(rhs)
将隐式转换为base1&&
(分别为base2&&
)。
编辑:当我在一个基类中有一个与X&&
完全匹配的模板构造函数时,这个假设实际上已经让我困扰了几次。显式地执行转换会更安全(尽管非常学究):
X::X(X&& rhs)
: base1(std::move(static_cast<base1&>(rhs)))
, base2(std::move(static_cast<base2&>(rhs)))
, mbr1(std::move(rhs.mbr1))
, mbr2(std::move(rhs.mbr2))
{}
或者仅仅是:
X::X(X&& rhs)
: base1(static_cast<base1&&>(rhs))
, base2(static_cast<base2&&>(rhs))
, mbr1(std::move(rhs.mbr1))
, mbr2(std::move(rhs.mbr2))
{}
我认为,如果除了base1
/base2
/mbr1
/mbr2
.之外没有其他基类或成员,那么它应该准确地复制编译器为X(X&&) = default;
隐式生成的内容
再次编辑: C++11§12.8/15描述了隐式成员式复制/移动构造函数的确切结构。
发布于 2014-01-25 10:14:31
这取决于继承层次结构。但是很有可能这段代码是好的。这是一个完整的演示,表明它是安全的(对于这个特定的演示):
#include <iostream>
struct base1
{
base1() = default;
base1(base1&&) {std::cout << "base1(base1&&)\n";}
};
struct base2
{
base2() = default;
base2(base2&&) {std::cout << "base2(base2&&)\n";}
};
struct br1
{
br1() = default;
br1(br1&&) {std::cout << "br1(br1&&)\n";}
};
struct br2
{
br2() = default;
br2(br2&&) {std::cout << "br2(br2&&)\n";}
};
struct X
: public base1
, public base2
{
br1 mbr1;
br2 mbr2;
public:
X() = default;
X(X&& rhs)
: base1(std::move(rhs))
, base2(std::move(rhs))
, mbr1(std::move(rhs.mbr1))
, mbr2(std::move(rhs.mbr2))
{ }
};
int
main()
{
X x1;
X x2 = std::move(x1);
}
哪一项应该输出:
base1(base1&&)
base2(base2&&)
br1(br1&&)
br2(br2&&)
在这里,您可以看到每个基座和每个成员恰好移动了一次。
记住:std::move
并不是真的移动。这只是一个向右值的强制转换,仅此而已。
因此代码强制转换为右值X
,然后将其向下传递给基类。假设基类看起来像我上面概述的那样,那么有一个隐式的类型转换到右值base1
和base2
,这将移动构造这两个单独的基类。
另外,
请记住:移出对象处于有效但未指定的状态。
只要base1
和base2
的移动构造函数不进入派生类并更改mbr1
或mbr2
,那么这些成员仍然处于已知状态,并准备从中移动。没问题。
现在我确实提到了可能会出现问题。这是如何实现的:
#include <iostream>
struct base1
{
base1() = default;
base1(base1&& b)
{std::cout << "base1(base1&&)\n";}
template <class T>
base1(T&& t)
{std::cout << "move from X\n";}
};
struct base2
{
base2() = default;
base2(base2&& b)
{std::cout << "base2(base2&&)\n";}
};
struct br1
{
br1() = default;
br1(br1&&) {std::cout << "br1(br1&&)\n";}
};
struct br2
{
br2() = default;
br2(br2&&) {std::cout << "br2(br2&&)\n";}
};
struct X
: public base1
, public base2
{
br1 mbr1;
br2 mbr2;
public:
X() = default;
X(X&& rhs)
: base1(std::move(rhs))
, base2(std::move(rhs))
, mbr1(std::move(rhs.mbr1))
, mbr2(std::move(rhs.mbr2))
{ }
};
int
main()
{
X x1;
X x2 = std::move(x1);
}
在这个例子中,base1
有一个模板化的构造函数,它接受一个右值。如果此构造函数可以绑定到右值X
,并且此构造函数将从右值X
,移动,那么就有问题了:
move from X
base2(base2&&)
br1(br1&&)
br2(br2&&)
解决这个问题的方法是使用forward<base1>(rhs)
而不是move(rhs)
(虽然相对较少,但也不是非常罕见)
X(X&& rhs)
: base1(std::forward<base1>(rhs))
, base2(std::move(rhs))
, mbr1(std::move(rhs.mbr1))
, mbr2(std::move(rhs.mbr2))
{ }
现在base1
看到的是右值base1
,而不是右值X
,它将绑定到base1
移动构造函数(假设它存在),因此您将再次获得:
base1(base1&&)
base2(base2&&)
br1(br1&&)
br2(br2&&)
一切再一次与这个世界融为一体。
https://stackoverflow.com/questions/21345515
复制相似问题