调用专门的 std::move()

Invoking specialized std::move()

我对下面示例中模板参数推导的方式感到困惑。我在 post 的其余部分使用术语 invoke 来暗示 实例化和调用.

我将 std::move() 专门用于我的自定义类型 my_type 并且我观察到,对于类型 my_type:

的实例 x
  1. std::move(x) 继续调用通用模板
  2. std::move(static_cast<my_type&&>(x))std::move(std::forward(x)) 调用特化
  3. 在我没有专业化的情况下,上述所有调用都调用了通用模板

我的问题是:

完整代码如下:

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

输出:

Execute 'auto b = std::move(a);'
Execute 'auto c = std::move(static_cast<my_type&&>(a));'
Invoke std::move() specialization

当您调用 std::move(a) 时,a 的类型是 my_type&,而不是 my_type&&。因此,通用 std::move 是更好的匹配,因为它可以精确匹配。

如果您将 move 的重载更改为如下所示:

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

然后它会被适当地调用(但是通用的会被调用 std::move(static_cast<my_type&&>(a));

发生这种情况是因为 generic definition 看起来像这样:

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

T&& 是关键。在类型推导的上下文中,它可以绑定到 my_type&my_type&& 或任何 cv(constvolatile)变体。这就是为什么在没有专门化的情况下,它能够为两个调用调用通用版本。

因此,要真正涵盖所有基础,您需要不止一个重载。不过,可能 custom_move 限制您的类型会更好。

因此,您的第一个问题是 std 中事物的任何专业化都必须遵守您正在专业化的事物的要求。这意味着......你不能做任何不同的事情。

其次,std::move的通用版本使用完美转发。专业化不能。

#define SPEC_MOVE(X) \
template<> inline \
typename std::remove_reference<X>::type&& move<X>(X t) noexcept \
{ \
  std::cout << "Invoke std::move() specialization\n"; \
  return static_cast<typename remove_reference<X>::type&&>(t); \
}
SPEC_MOVE(my_type&&)
SPEC_MOVE(my_type&)
SPEC_MOVE(my_type const&)
SPEC_MOVE(my_type const&&)
SPEC_MOVE(my_type const volatile&&)
SPEC_MOVE(my_type const volatile&)
SPEC_MOVE(my_type volatile&)
SPEC_MOVE(my_type volatile&&)

应该可以了。

这是个糟糕的计划。