防止C ++ 03代码在C ++ 11中执行次优的好方法?

时间:2014-01-22 20:28:03

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

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

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

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

(我想避免使用预处理程序,因为它们会很难看,因为我不仅需要测试_MSC_VER >= 1600之类的编译器版本,还因为它们不适用于像{{{}}这样的工具。 {3}}无法识别C ++ 11移动语法但迫使我预处理代码。)

2 个答案:

答案 0 :(得分:10)

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

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

这是一个简单的头文件,在启用或禁用C ++ 11的情况下,clanggcc可以快速编译而不会发出任何警告:

// 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中的右值引用,实际上一个右值引用(即T&& )对于C ++ 11。它不会完全处理C ++ 03 rvalue引用,但至少允许移动构造函数并移动赋值,而不需要实际需要rvalue引用。

请注意,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);
}

也就是说,实际的业务逻辑没有任何预处理器hackery。使用预处理器的唯一需要是在标题movetools.hpp中,不需要混淆。也就是说,我实际上认为它使用预处理器黑客来定义实际的移动构造函数或移动赋值,尽管预处理器在某处使用。如果您坚持不想使用宏hackery,可以通过指示编译器查看不同的头文件来完成,但这是movetools.hpp的实现细节。

答案 1 :(得分:3)

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