Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How safe is this method of emulating move-semantics in C++03?

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

like image 587
user541686 Avatar asked Dec 23 '13 11:12

user541686


People also ask

Does C have move semantics?

In C++11, the resources of the objects can be moved from one object to another rather than copying the whole data of the object to another. This can be done by using move semantics in C++11. Move semantics points the other object to the already existing object in the memory.

What is the purpose of std :: move?

std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t . It is exactly equivalent to a static_cast to an rvalue reference type.

What is Move semantics in C++?

Move semantics allows you to avoid unnecessary copies when working with temporary objects that are about to evaporate, and whose resources can safely be taken from that temporary object and used by another.

Does STD copy move?

std::move is actually just a request to move and if the type of the object has not a move constructor/assign-operator defined or generated the move operation will fall back to a copy.


1 Answers

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.

like image 101
Yakk - Adam Nevraumont Avatar answered Oct 19 '22 06:10

Yakk - Adam Nevraumont