【发布时间】:2013-04-09 22:27:03
【问题描述】:
std::is_move_constructible<T>::value == true 是否暗示T 有一个可用的移动构造函数?
如果是这样,它的默认行为是什么?
考虑以下情况:
struct foo {
int* ptr;
};
int main() {
{
std::cout << std::is_move_constructible<foo>::value << '\n';
foo f;
f.ptr = (int*)12;
foo f2(std::move(f));
std::cout << f.ptr << ' ' << f2.ptr << '\n';
}
return 0;
}
输出是:
1
0000000C 0000000C
我认为f.ptr 应该是nullptr。
所以在这种情况下,
-
f2move 构造了吗? - 如果是,右值不应该失效吗?
- 我如何知道一个类的实例是否可以正确移动构造(使旧实例无效)?
(我正在使用 VS11。)
更新
移动构造函数的默认行为与复制构造函数相同,是否正确? 如果是真的,
- 我们总是希望移动 ctor 窃取已移动对象的资源,而默认移动 ctor 的行为与预期不符,那么拥有默认移动 ctor 有什么意义?
- 我如何知道一个类是否有自定义移动构造函数(可以保证其行为正常)?
当我声明一个时,foo f2(std::move(f)); 似乎调用了复制 ctor,请参阅:
struct foo {
int* ptr;
foo() {}
foo(const foo& other) {
std::cout << "copy constructed\n";
}
};
int main() {
{
std::cout << std::is_move_constructible<foo>::value << '\n';
foo f;
foo f2(std::move(f));
}
system("pause");
return 0;
}
现在的输出是:
1
copy constructed
如果foo 有一个移动构造函数,那么foo f2(std::move(f)) 不会调用它吗?
所以现在我的问题是: 如何知道一个类是否有移动 ctor,如果有,我如何显式调用它?
我想做的是……
template<typename T, bool has_move_ctor>
struct MoveAux;
template<typename T>
struct MoveAux<T, true> {
static void doMove(T* dest, T* src) {
new(dest) T(std::move(*src)); //move ctor
}
};
template<typename T>
struct MoveAux<T, false> {
static void doMove(T* dest, T* src) {
new(dest) T(*src); //copy ctor
src->~T();
}
};
template<typename T>
inline doMove(T* dest, T* src) {
MoveAux<T,/*a trait*/>::doMove(dest, src);
}
所以我认为std::is_move_constructible<T>::value 可以传递给模板,而现在我看到这个特征只关心T t(T()) 是否是一个有效的表达式,它可能会调用T::T(const T&)。
现在假设T 是一个自定义类,那么我希望上述模板的行为类似于:
- 如果我不声明移动 ctor,我希望该模板方法调用
MoveAux<T,false>::doMove。 - 如果我声明了一个,我需要它调用
MoveAux<T,true>::doMove。
有没有可能实现这个功能?
【问题讨论】:
-
@DavidSchwartz:“将对象传递给
move后,您可能无法访问其任何成员”是不正确的。从一个对象移动后,您可能无法访问它的任何成员,这甚至不是真的。从原始类型移动的效果是使其保持不变。类类型可以自己决定移动的效果——标准库类大多说对象处于不确定但有效的状态,通常至少析构函数需要工作,这意味着任何析构函数访问的成员将必须持有合理的值。 -
@DavidSchwartz:“在传递一个要移动的对象后,您可能无法访问它的任何成员。”这根本不是真的。上述代码具有 C++ 规范明确定义的行为。移动构造函数将复制指针,从而定义原始对象的状态。
标签: c++ c++11 move-semantics