質問

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.)

役に立ちましたか?

解決 2

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

他のヒント

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top