问题似乎真的是:如何使用 C++03 编译器实现移动构造函数和移动赋值?
简单的答案是:他们不能!但是,简单的答案忽略了创建完全有效的 C++03 代码并成为移动构造函数并使用 C++11 编译器移动赋值的可能性。这种方法需要使用一些预处理程序,但该位仅用于创建一个标头,定义一些用于实际实现的工具。
这是一个简单的头文件,在启用或禁用 C++11 的情况下,clang 和 gcc 可以愉快地编译而没有任何警告:
// file: movetools.hpp
#ifndef INCLUDED_MOVETOOLS
#define INCLUDED_MOVETOOLS
INCLUDED_MOVETOOLS
namespace mt
{
#if __cplusplus < 201103L
template <typename T>
class rvalue_reference {
T* ptr;
public:
rvalue_reference(T& other): ptr(&other) {}
operator T&() const { return *this->ptr; }
};
#else
template <typename T>
using rvalue_reference = T&&;
#endif
template <typename T>
rvalue_reference<T> move(T& obj) {
return static_cast<rvalue_reference<T> >(obj);
}
}
#endif
基本功能是定义一个模板mt::rvalue_reference<T>,它的行为有点像 C++03 中的右值引用,而实际上是一个用于 C+ 的右值引用(即T&&) +11。它不会完全处理 C++03 右值引用,但至少允许在不需要右值引用的情况下定义移动构造函数和移动赋值。
请注意,mt::move() 仅用于稍后显示如何在 C++03 中移动 rvalue_reference<T>!重点是rvalue_reference<T> 要么是 C++03 编译器可以理解的东西,要么是 T&&。对于这种相当合理的表示法,编译器必须支持别名模板。如果不是这种情况,可以应用相同的技巧,但使用相应类模板的合适嵌套类型。
这是一个使用这个头的例子:
#include "movetools.hpp"
#include <iostream>
class foo
{
public:
foo() { std::cout << "foo::foo()\n"; }
foo(foo const&) { std::cout << "foo::foo(const&)\n"; }
foo(mt::rvalue_reference<foo> other) {
std::cout << "foo::foo(&&)\n";
this->swap(other);
}
~foo() { std::cout << "foo::~foo()\n"; }
foo& operator= (foo const& other) {
std::cout << "foo::operator=(foo const&)\n";
foo(other).swap(*this);
return *this;
}
foo& operator= (mt::rvalue_reference<foo> other) {
std::cout << "foo::operator=(foo&&)\n";
this->swap(other);
return *this;
}
void swap(foo&) {
std::cout << "foo::swap(foo&)\n";
}
};
int main()
{
foo f0;
foo f1 = f0;
foo f2 = mt::move(f0);
f1 = f2;
f0 = mt::move(f1);
}
也就是说,实际的业务逻辑没有任何预处理器骇客。唯一需要处理预处理器的是标题movetools.hpp,不需要弄乱。也就是说,我实际上认为它确实不使用预处理器黑客来定义实际的移动构造函数或移动赋值,尽管预处理器在某处使用。如果您坚持不想使用宏hackery,可以通过指示编译器查看不同的标头来完成,但这是movetools.hpp 的实现细节。