调用专门的 std::move()
Invoking specialized std::move()
我对下面示例中模板参数推导的方式感到困惑。我在 post 的其余部分使用术语 invoke 来暗示 实例化和调用.
我将 std::move()
专门用于我的自定义类型 my_type
并且我观察到,对于类型 my_type
:
的实例 x
std::move(x)
继续调用通用模板
std::move(static_cast<my_type&&>(x))
或 std::move(std::forward(x))
调用特化
- 在我没有专业化的情况下,上述所有调用都调用了通用模板
我的问题是:
- 为什么上面第 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() 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(const
或 volatile
)变体。这就是为什么在没有专门化的情况下,它能够为两个调用调用通用版本。
因此,要真正涵盖所有基础,您需要不止一个重载。不过,可能 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&&)
应该可以了。
这是个糟糕的计划。
我对下面示例中模板参数推导的方式感到困惑。我在 post 的其余部分使用术语 invoke 来暗示 实例化和调用.
我将 std::move()
专门用于我的自定义类型 my_type
并且我观察到,对于类型 my_type
:
x
std::move(x)
继续调用通用模板std::move(static_cast<my_type&&>(x))
或std::move(std::forward(x))
调用特化- 在我没有专业化的情况下,上述所有调用都调用了通用模板
我的问题是:
- 为什么上面第 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() 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(const
或 volatile
)变体。这就是为什么在没有专门化的情况下,它能够为两个调用调用通用版本。
因此,要真正涵盖所有基础,您需要不止一个重载。不过,可能 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&&)
应该可以了。
这是个糟糕的计划。