Насколько безопасен этот метод эмуляции семантики перемещения в C++03?

используя ответ, я изобрел свой собственный метод эмуляции семантики перемещения в C++03 на основе swap.

во-первых, я обнаруживаю семантику перемещения (т. е. доступность 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?

(Примечание: я ищу практический ответ, а не ответ на язык-юрист. Я знаю, что определение новых членов в namespace std технически не определено, но на практике это не вызовет проблемы с любым компилятором, поэтому я не считаю, что стоит беспокоиться об этом для целей этого вопроса. Я беспокоюсь о таких случаях, как случайные болтающиеся ссылки и тому подобное.)

2 ответов


move не двигается, но ваш move делает. Это означает, что в C++11 есть std::move на выражение, которое не назначает его нигде, или потребитель не изменяет данные. Ваш.

что хуже, что ваш move блокирует rvo / nrvo, как и C++11. В C++11 ваш оператор return из test было бы плохой идеей, так как возвращаемое значение было бы неявно moveed. В C++03, поскольку nrvo блокируется по аргументам, это было бы оптимальным. Поэтому использование двух отличающийся.

код std::move return value experiences reference lifetime extension, в то время как возвращаемое значение в C++11 этого не делает. Код должен быть полностью протестирован в обоих.


резюме, кажется, "это безопасно, пока вы используете его обычным способом".