【问题标题】:Good way to prevent C++03 code from performing suboptimally in C++11?防止 C++03 代码在 C++11 中表现不佳的好方法?
【发布时间】:2014-02-13 02:56:12
【问题描述】:

我有一些 C++03 代码为某些类实现了swap,以使std::sort(和其他功能)更快。

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

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

(我想避免使用前处理器黑客攻击,因为它们会很丑陋,因为我不仅必须测试 _MSC_VER >= 1600 之类的编译器版本,而且还因为它们不能很好地与 LZZ 之类的工具一起使用不识别 C++11 移动语法但强迫我预处理代码。)

【问题讨论】:

  • 根本不定义任何特殊成员,只让它们生成(通过使用适当的构建块)?
  • 很遗憾,std::sort 不是强制使用 swap,那么它有什么帮助呢?
  • @Xeo:不幸的是,Visual C++ 不会自动生成移动成员。
  • @TemplateRex:我不确定,你有什么建议吗?
  • 我认为移动可以通过与临时交换来实现。这只是一个技巧,但您可以提供特殊的迭代器或包装器来提供这种移动构造/分配。 (对于无法有效默认构造的类来说,这可能会很棘手。)

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


【解决方案1】:

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

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

这是一个简单的头文件,在启用或禁用 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&lt;T&gt;,它的行为有点像 C++03 中的右值引用,而实际上一个用于 C+ 的右值引用(即T&amp;&amp;) +11。它不会完全处理 C++03 右值引用,但至少允许在不需要右值引用的情况下定义移动构造函数和移动赋值。

请注意,mt::move() 仅用于稍后显示如何在 C++03 中移动 rvalue_reference&lt;T&gt;!重点是rvalue_reference&lt;T&gt; 要么是 C++03 编译器可以理解的东西,要么是 T&amp;&amp;。对于这种相当合理的表示法,编译器必须支持别名模板。如果不是这种情况,可以应用相同的技巧,但使用相应类模板的合适嵌套类型。

这是一个使用这个头的例子:

#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 的实现细节。

【讨论】:

  • @dyp: 不是mt::move()在示例中用于显示移动构造函数和移动运算符的调用方式!关键是:对于具有右值引用和别名模板的东西(例如,C++11 编译器;但是,没有别名模板也可以使用嵌套类型来完成),使用 mt::rvalue_reference&lt;T&gt; 作为参数的函数实际上采用 @ 987654335@ 作为参数。也就是说,您的第二条评论完全解决了目标!
  • 我会收回那条评论,有点困惑。这实际上是一个很好的解决方案。 (您甚至不必在 C++03 模式下提供伪移动支持,这些 ctor 只需编译即可。)
  • 编译器支持move:它只是不会自动生成移动ctor。
  • +1 谢谢 :) 并不完美,但我真的不希望有更好的方法,所以我会研究一下。
  • @Yakk:启用了 C++11 的编译器确实支持移动构造和移动赋值。但是,该代码还可以使用不理解右值引用的 C++03 编译器进行编译。使用我描述的 C++03 编译器的方法,可以将代码编译为移动构造函数并使用 C++11 编译器进行赋值。
【解决方案2】:

看看Boost.Move。它具有 c++03 的移动仿真。也许它可以帮助,但我没有看详细信息。

【讨论】:

  • +1 是的,我以前见过,但是使用 Boost 对于我想要的东西来说太过分了。
猜你喜欢
  • 1970-01-01
  • 2014-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
相关资源
最近更新 更多