调用专门的std :: move()

时间:2017-05-10 22:30:31

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

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

我将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. 如果没有我的专业化,所有上述调用都会调用通用模板
  4. 我的问题是:

    • 为什么上面第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
    

2 个答案:

答案 0 :(得分:5)

当您致电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,你可能会更好。

答案 1 :(得分:1)

所以,你的第一个问题是(bool IsDivisibleBy3, bool IsDivisibleBy5) match = (i % 3 == 0, i % 5 == 0); switch (match) { case var _ when match.IsDivisibleBy3 && match.IsDivisibleBy5: // FizzBuzz break; case var _ when match.IsDivisibleBy3: // Fizz break; case var _ when match.IsDivisibleBy5: // Buzz break; default: // break; } 中任何事物的专业化都必须遵守你所专注的事物的要求。这意味着......你不能做任何不同的事情。

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

std::move

应该这样做。

这是一个糟糕的计划。