这种在C ++ 03中模拟移动语义的方法有多安全?

时间:2013-12-23 11:41:59

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

使用this answer,我发明了自己的基于swap在C ++ 03中模拟移动语义的方法。

首先,我检测移动语义(即C ++ 03的可用性):

#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) ||  \
    defined(_MSC_VER) && _MSC_VER >= 1600
#define HAS_MOVE_SEMANTICS 1
#elif defined(__clang)
#if __has_feature(cxx_rvalue_references)
#define HAS_MOVE_SEMANTICS 1
#else
#define HAS_MOVE_SEMANTICS 0
#endif
#else
#define HAS_MOVE_SEMANTICS 0
#endif

然后我有条件地定义了一个名为move的宏:

#if !HAS_MOVE_SEMANTICS
#include <algorithm>
namespace _cpp11_detail
{
    template<bool B, class T = void> struct enable_if;
    template<class T> struct enable_if<false, T> { };
    template<class T> struct enable_if<true, T> { typedef T type; };
    template<class T>
    inline char (&is_lvalue(
        T &, typename std::allocator<T>::value_type const volatile &))[2];
    inline char (&is_lvalue(...))[1];
    template<bool LValue, class T>
    inline typename enable_if<!LValue, T>::type move(T v)
    { T r; using std::swap; swap(r, v); return r; }
    template<bool LValue, class T>
    inline typename enable_if<LValue, T>::type move(T &v)
    { T r; using std::swap; swap(r, v); return r; }
    template<bool LValue, class T>
    inline typename enable_if<LValue, T>::type const &move(T const &v)
    { return v; }
}
using _cpp11_detail::move;
namespace std { using _cpp11_detail::move; }
// Define this conditionally, if C++11 is not supported
#define move(...) move<  \
    (sizeof((_cpp11_detail::is_lvalue)((__VA_ARGS__), (__VA_ARGS__))) != 1)  \
>(__VA_ARGS__)
#endif

然后我像这样使用它:

#include <vector>

std::vector<int> test(std::vector<int> v) { return std::move(v); }

int main()
{
    std::vector<int> q(5, 5);
    int x = 5;
    int y = std::move(x);
    std::vector<int> qq = test(std::move(test(std::move(q))));
}

我的问题是, 安全在实践中是如何实现的?假设编译正常

  1. 是否有任何实际情况可能无法在C ++ 03中正常工作但在C ++ 11中无法正常工作?

  2. 相反的情况 - 它可以在C ++ 11中正常工作但在C ++ 03中失败吗?

  3. 注意:我正在寻找一个实用的答案,而不是语言律师的答案。我知道在namespace std中定义新成员在技术上是未定义的,但在实践中这不会对任何编译器造成问题,所以我发现这个问题的目的并不值得担心。我担心偶然悬挂引用等情况。)

2 个答案:

答案 0 :(得分:3)

move不会移动,但您的move会移动。这意味着在C ++ 11中,表达式上的std::move不会将其分配到任何地方,或者消费者不会修改数据。你的确如此。

更糟糕的是,你的move阻止了rvo / nrvo,就像C ++ 11一样。在C ++ 11中,来自test的返回语句是个坏主意,因为返回值将隐式move。在C ++ 03中,由于nrvo在参数上被阻塞,因此它是最佳的。所以两者的使用是不同的。

您的std::move返回值会经历引用生命周期扩展,而C ++ 11中的返回值则不会。代码必须在两者中进行全面测试。

答案 1 :(得分:0)

执行摘要似乎是,“只要你按常规方式使用它就是安全的。”