【问题标题】:Invoking specialized std::move()调用专门的 std::move()
【发布时间】:2017-10-09 18:21:08
【问题描述】:

我对以下示例中如何进行模板参数推导感到困惑。我在本文的其余部分使用术语 invoke 来暗示 实例化和调用

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

  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() 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?如果你希望你的脚趾是可移动的,只需提供一个移动构造函数和移动赋值运算符。
  • @NathanOliver,我同意自定义移动构造函数和赋值运算符是正确的方法。但无论如何,我无法解释我在观察什么。这就是我发布问题的原因。
  • @NathanOliver:我认为脚趾已经可以移动了 :-)
  • @AndyG 什么是脚趾?
  • @ACE:这是附着在你脚上的一种摇摆不定的东西;-)

标签: c++ c++11 stl move-semantics template-specialization


【解决方案1】:

当您调用 std::move(a) 时,a 的类型是 my_type&amp;,而不是 my_type&amp;&amp;。因此,通用的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&lt;my_type&amp;&amp;&gt;(a));

这是因为generic definition 看起来像这样:

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

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

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

【讨论】:

  • 1.如果 amy_type&amp; 并且 not my_type&amp;&amp;,那么它为什么/如何绑定到需要 T&amp;&amp; 而不是 @987654343 的通用移动模板@ 2. 我确实尝试用my_type&amp; 重载move,但编译器出错,提示找不到匹配的模板来专门化:(aka 'my_type &amp;&amp;(my_type &amp;&amp;) noexcept') against ... (aka 'my_type &amp;&amp;(my_type &amp;) noexcept')
  • @ACE:我回答 1) 已经在帖子中。 “在类型推导的上下文中,它可以绑定到 my_type&、my_type&& 或任何 cv 变体”
  • @ACE:关于 2)您可能没有正确编写它(总是更喜欢重载而不是模板专业化):试试这个:wandbox.org/permlink/DlkWb1EyZWDZEOFA
  • 啊,你是在重载而不是模板专门化
  • @ACE:不需要重载。只是......模板专业化更奇怪:wandbox.org/permlink/M7VnfDdezjW0nhk6
【解决方案2】:

所以,您的第一个问题是 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&&)

应该可以的。

这是一个糟糕的计划。

【讨论】:

  • Specializations cannot use perfect forwarding加1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
  • 1970-01-01
相关资源
最近更新 更多