Pergunta

Using this answer, I invented my own method of emulating move-semantics in C++03 based on swap.

First, I detect move-semantics (i.e. availability of 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

Then I conditionally define a macro called 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

Then I use it like this:

#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))));
}

My question is, how safe is this approach in practice? (assuming it compiles fine)

  1. Are there any practical scenarios in which it could fail to work correctly in C++03 but not in C++11?

  2. What about the opposite -- can it work correctly in C++11 but fail in C++03?

(Note: I'm looking for a practical answer, not a language-lawyer answer. I'm aware that defining new members in namespace std is technically undefined, but in practice that won't cause issues on any compiler so I don't find that worth worrying about for the purposes of this question. I'm worried about cases such as accidental dangling references and the like.)

Foi útil?

Solução 2

The executive summary seems to be, "it's safe as long as you use it the usual way".

Outras dicas

move does not move, but your move does. This means that in C++11 a std::move on an expression that does not assign it anywhere, or the consumer does not modify data. Yours does.

What is worse is that your move blocks rvo/nrvo, just like C++11. In C++11 your return statement from test would be a bad idea, as the return value would be implicitly moveed. In C++03, as nrvo is blocked on arguments, it would be optimal. So the use of the two is different.

Your std::move return value experiences reference lifetime extension, while the return value in C++11 does not. Code will have to be fully tested in both.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top