模板完美转发类

Perfect forwarding with template classes

假设我有一个模板class,例如 template<int n, int m> class Matrix

有没有办法定义矩阵乘法运算符 * 以便

  1. * 的参数可以是左值或右值引用
  2. *从其参数
  3. 推断出适当的return类型(即适当的模板参数)

我的想法是这样的

template< int n,int k, int m, template<int,int> class T1, template<int, int> class T2, template<int,int>class T3 >
 T3<n,m> operator*(T1<n,k>&&, T2<k,m>&&)//does not work

当我尝试 运行 上面的代码(以明显的方式填充主体)时,我得到一个错误:

Cannot convert from Matrix<1,1> to Matrix<1,1>&&

当参数是左值时。

是的。来自我自己的代码:

template
<
    int LeftColumnsRightRows, int LeftRows,
    int RightColumns
>
Matrix<RightColumns, LeftRows> operator*(Matrix<LeftColumnsRightRows, LeftRows> const& a, Matrix<RightColumns, LeftColumnsRightRows> const& b)

而且我不知道您为什么要花 && 秒。如果要将其他两种类型转换为矩阵,然后将它们相乘,则应在乘法运算符之外进行转换。

正如之前的回答所解释的那样,我也会简单地坚持 const 引用。但是为了阐明为什么您的代码不起作用,完美转发仅在您使用 对 cv 不合格模板参数的右值引用 时适用。通俗地说,它必须只是 T&&,其中 T 是函数模板参数:

template<class T>
void ForwardMe(T&& t)
{
    DoSomething(std::forward<T>(t));
}

这个想法是,当传递一个左值时,编译器将能够将 T 推断为 type&(因此由于引用折叠规则,函数签名变为 void ForwardMe(type&)),或者在右值的情况下只是 type(签名变为 void ForwardMe(type&&))。

在您的示例中,您执行如下操作:

template<int N, template<int> class T>
void ForwardMe(T<N>&& t)
{
    // ...
}

这并没有像您预期的那样工作,因为编译器无法推断出 T 是对某物的引用,因此您无法进行完美转发。因此,函数参数 t 将仅匹配右值引用。

由于 const 引用可以绑定到临时对象,因此在上面的示例中使用 const T<N>& 将解决您的问题。但是如果你真的想同时支持左值和右值输入(因为你喜欢在适当的地方使用移动语义),你有两个选择:

  • 为所有 4 种排列编写重载:左值*左值、左值*右值、右值*左值、右值*右值。
  • 编写通用函数模板,使用SFINAE限制输入类型

后者类似于:

#include <type_traits>

template<class L, class R>
struct MatrixMulResult_helper;
template<int n, int m, int k, template<int, int> class T>
struct MatrixMulResult_helper<T<n, m>, T<m, k>> { using type = T<n, k>; };

template<class L, class R>
using MatrixMulResult = typename MatrixMulResult_helper<L, R>::type;

template<class L, class R>
MatrixMulResult<std::decay_t<L>, std::decay_t<R>>
operator*(L&& lhs, R&& rhs)
{
    // ...
}

编译器现在可以自由推断 LR 作为参考。 MatrixMulResult<> 确保仅当 LR 的(衰减类型)分别具有 T<n,m>T<m,k> 形式时才定义此函数。它returns一个T<n,k>.