Reputation: 431
Suppose I have a template class, for example
template<int n, int m> class Matrix
Is there a way to define a matrix multiplication operator * so that
What I have in mind is something like
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
When I try to run the above code (with the bodies filled in in the obvious ways), I get an error:
Cannot convert from Matrix<1,1> to Matrix<1,1>&&
when the arguments are lvalues.
Upvotes: 0
Views: 811
Reputation: 1376
I would simply stick to const references as well, as explained by previous answer. But to clarify why your code doesn't work, perfect forwarding only applies when you use an rvalue reference to an cv-unqualified template parameter. In layman's terms, it has to be just T&&
, where T
is a function template parameter:
template<class T>
void ForwardMe(T&& t)
{
DoSomething(std::forward<T>(t));
}
The idea is that the compiler will be able to deduce T
as type&
when passed a lvalue (so the function signature becomes void ForwardMe(type&)
because of reference collapsing rules), or just type
in case of rvalues (signature becomes void ForwardMe(type&&)
).
In your example, you do something like:
template<int N, template<int> class T>
void ForwardMe(T<N>&& t)
{
// ...
}
This doesn't work as you had expected, because the compiler cannot deduce T
to be a reference to something, so you can't have perfect forwarding. The function parameter t
will therefore only match rvalue references.
Since const references can bind to temporaries, using const T<N>&
instead in above example will solve your problems. But if you really want to support both lvalue and rvalue inputs (because you like to have move semantics where appropriate), you have two options:
The latter would be something like:
#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)
{
// ...
}
The compiler is now free to deduce L
and R
as references. MatrixMulResult<>
makes sure that this function is only defined when (the decayed types of) L
and R
are respectively of the form T<n,m>
and T<m,k>
. It returns a T<n,k>
.
Upvotes: 2
Reputation: 1366
Yes. From my own code:
template
<
int LeftColumnsRightRows, int LeftRows,
int RightColumns
>
Matrix<RightColumns, LeftRows> operator*(Matrix<LeftColumnsRightRows, LeftRows> const& a, Matrix<RightColumns, LeftColumnsRightRows> const& b)
And I don't know why you'd want it to take &&
s. If you want to convert two other types to matrices and then multiply them, you should do the conversion outside of the multiplication operator.
Upvotes: 2