防止 C++03 代码在 C++11 中表现不佳的好方法?

Good way to prevent C++03 code from performing suboptimally in C++11?

本文关键字:方法 代码 C++03 C++11 防止      更新时间:2023-10-16

我有一些C++03代码为某些类实现了swap,以使std::sort(和其他函数)快速完成。

对我来说不幸的是,std::sort现在似乎使用std::move,这意味着我的代码现在比 C++03 中慢得多

我知道我可以使用#if __cplusplus >= 201103L有条件地定义移动构造函数/移动赋值运算符,但我想知道是否有更好的不使用预处理器黑客的方法?

(我想避免使用proprocessor黑客,因为它们会很丑陋,因为我不仅要测试像_MSC_VER >= 1600这样的编译器版本,而且还因为它们不能很好地与LZZ等无法识别C++11移动语法但迫使我预处理代码的工具一起使用。

似乎真正的问题是:如何使用 C++03 编译器实现移动构造函数和移动赋值?

简单的答案是:他们不能!然而,简单的答案忽略了创建完全有效的 C++03 代码的可能性,这些代码成为移动构造函数并使用 C++11 编译器移动赋值。这种方法需要使用一些预处理器黑客,但该位仅用于创建一个标头,定义用于实际实现的一些工具。

这是一个简单的头文件,它很高兴地编译,没有任何警告,clang和gcc启用或禁用C++11:

// 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++11 的右值引用(即T&&)。它不会完全处理 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中,不需要弄乱。也就是说,我实际上认为它没有使用预处理器黑客来定义实际的移动构造函数或移动赋值,尽管预处理器在某处使用。如果您坚持不想使用宏黑客,可以通过指示编译器查看不同的标头来完成,但这是movetools.hpp的实现细节。

看看 Boost.Move。它具有针对 c++03 的移动仿真。也许它可以提供帮助,但我没有看细节。