Invoking specialized std::move()

c++, c++11, move-semantics, stl, template-specialization

Solution

When you call `std::move(a)` the type of `a` is `my_type&`, not `my_type&&`. Therefore the generic `std::move` is a better match because it can match exactly.

If you changed your overload of `move` to look like this:

inline
typename std::remove_reference<my_type>::type&&
move(my_type& t) noexcept
{
    std::cout << "Invoke std::move() specialization\n";
    return static_cast<typename remove_reference<my_type>::type&&>(t);
}

Then it would appropriately get called (but the the generic one would get called for `std::move(static_cast<my_type&&>(a));`)

This happens because the generic definition looks like this:

template< class T >
constexpr typename std::remove_reference<T>::type&& move( T&& t );

That `T&&` is key. In the context of type deduction it can bind to both `my_type&`, `my_type&&` or any cv (`const` or `volatile`) variation. This is why in the absence of the specialization it's able to invoke the generic version for both calls.

So to really cover all bases, you'll need more than one overload. Probably, though, you'd be better off with a `custom_move` constrained for your type.

Problem

I am confused about how template argument deduction is taking place in the example below. I use the term invoke in the rest of this post to imply instantiate and invoke. I specialized `std::move()` for my custom type `my_type` and I observe that, for an instance `x` of type `my_type`: - `std::move(x)` continues to invoke the generic template - `std::move(static_cast<my_type&&>(x))` or `std::move(std::forward(x))` invokes the specialization - In the absence of my specialization, all of the above calls invoke the generic template My questions are: - Why does the call in item #1 above not invoke the specialization? - In the absence of the specialization, how do the calls in items #1 and #2 behave the same? Here is the entire code: ``` #include<iostream> #include<utility> struct my_type { int x; }; namespace std { // This is the std::move() definition in the preprocessor output: // // template <class _Tp> // inline __attribute__ ((__visibility__("hidden"), __always_inline__)) constexpr // typename remove_reference<_Tp>::type&& // move(_Tp&& __t) noexcept // { // typedef typename remove_reference<_Tp>::type _Up; // return static_cast<_Up&&>(__t); // } // This is std::move() specialized for my_type template<> inline typename std::remove_reference<my_type>::type&& move<my_type>(my_type&& t) noexcept { std::cout << "Invoke std::move() specialization\n"; return static_cast<typename remove_reference<my_type>::type&&>(t); } } // namespace std int main() { auto a = my_type(); std::cout << "Execute 'auto b = std::move(a);'\n"; auto b = std::move(a); // Invokes the generic template std::cout << "Execute 'auto c = std::move(static_cast<my_type&&>(a));'\n"; auto c = std::move(static_cast<my_type&&>(a)); // Invokes the specialization return 0; } ``` Output: ``` Execute 'auto b = std::move(a);' Execute 'auto c = std::move(static_cast<my_type&&>(a));' Invoke std::move() specialization ```

Original source