调用专用 std::move()

Invoking specialized std::move()

本文关键字:move 调用 std 专用      更新时间:2023-10-16

我对下面的示例中模板参数推导是如何发生的感到困惑。我在这篇文章的其余部分使用术语 invoke 来表示实例化和调用

我专门为我的自定义类型my_type专门std::move(),我观察到,对于类型 my_type 的实例x

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

我的问题是:

  • 为什么上面项目 #1 中的调用不调用专业化?
  • 在没有专业化的情况下,项目 #1 和 #2 中的调用如何表现相同?

以下是整个代码:

#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() specializationn";
    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() specializationn";
    return static_cast<typename remove_reference<my_type>::type&&>(t);
}

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

发生这种情况是因为通用定义如下所示:

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() specializationn"; 
  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&&)

这应该可以做到。

这是一个糟糕的计划。