完美转发模板类

时间:2017-12-28 20:57:39

标签: c++ templates rvalue-reference

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

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

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

    我想到的是像

    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
    

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

      

    无法转换为Matrix&lt; 1,1&gt;到Matrix&lt; 1,1&gt;&amp;&amp;

    当参数是左值时。

2 个答案:

答案 0 :(得分:2)

是。从我自己的代码:

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

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

答案 1 :(得分:2)

我也会坚持使用const引用,正如之前的回答所解释的那样。但为了澄清您的代码无法正常工作的原因,只有当您使用对cv-nonqualified模板参数的rvalue引用时,才能应用完美转发。在外行人的术语中,它必须只是T&&,其中T是函数模板参数:

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

这个想法是,当传递左值时,编译器将能够将T推导为type&(因为参考折叠规则,函数签名变为void ForwardMe(type&)),或者只是{ {1}}如果是rvalues(签名变为type)。

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

void ForwardMe(type&&)

这并不像您预期​​的那样有效,因为编译器无法推断template<int N, template<int> class T> void ForwardMe(T<N>&& t) { // ... } 是对某事物的引用,因此您无法完成转发。因此,函数参数T仅匹配右值引用。

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

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

后者将是这样的:

const T<N>&

编译器现在可以自由推断#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) { // ... } L作为参考。 R确保仅在MatrixMulResult<>L的衰变类型分别为RT<n,m>时定义此函数。它返回T<m,k>