模板完美转发类
Perfect forwarding with template classes
假设我有一个模板class,例如
template<int n, int m> class Matrix
有没有办法定义矩阵乘法运算符 * 以便
- * 的参数可以是左值或右值引用
- *从其参数
推断出适当的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)
{
// ...
}
编译器现在可以自由推断 L
和 R
作为参考。 MatrixMulResult<>
确保仅当 L
和 R
的(衰减类型)分别具有 T<n,m>
和 T<m,k>
形式时才定义此函数。它returns一个T<n,k>
.
假设我有一个模板class,例如
template<int n, int m> class Matrix
有没有办法定义矩阵乘法运算符 * 以便
- * 的参数可以是左值或右值引用
- *从其参数 推断出适当的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)
{
// ...
}
编译器现在可以自由推断 L
和 R
作为参考。 MatrixMulResult<>
确保仅当 L
和 R
的(衰减类型)分别具有 T<n,m>
和 T<m,k>
形式时才定义此函数。它returns一个T<n,k>
.