Reputation: 2134
I have a template class for which I'm overloading operator+
, but need it to potentially return a different data type than what the class is instantiated for. For example, the following snippet performs the standard mathematical definition of either vector*vector (inner product), vector*scalar, or scalar*vector. To be specific, let's say I have a Vector<int>
and the scalar is of type double
-- I want to return a double from the "vector*scalar" operator+
function.
I'm pretty sure I'm missing some template<class T>
statements for the friend functions(?), but this snippet isn't meant to compile.
template<class T>
class Vector
{
private:
std::vector<T> base;
public:
friend Vector operator*(const Vector& lhs, const Vector& rhs); // inner product
Vector<T> operator*(const T scalar); // vector*scalar
friend Vector operator*(const T scalar, const Vector& rhs); // scalar*vector
};
template<class T>
Vector<T> operator*(const Vector<T>& lhs, const Vector<T>& rhs) // inner product
{
assert( lhs.base.size() == rhs.base.size() );
Vector result;
result.base.reserve(lhs.base.size());
std::transform( lhs.base.begin(), lhs.base.end(), rhs.base.begin(), std::back_inserter(result.base), std::multiplies<T>() );
return result;
}
template<class T>
Vector<T> Vector<T>::operator*(const T scalar) // vector*scalar
{
Vector result;
result.base.reserve(base.size());
std::transform( base.begin(), base.end(), std::back_inserter(result.base), std::bind1st(std::multiplies<T>(), scalar) );
return result;
}
template<class T>
Vector<T> operator*(const T scalar, const Vector<T>& rhs) // scalar*vector
{
Vector result;
result.base.reserve(rhs.base.size());
std::transform( rhs.base.begin(), rhs.base.end(), std::back_inserter(result.base), std::bind1st(std::multiplies<T>(), scalar) );
return result;
}
Upvotes: 0
Views: 513
Reputation: 45675
I guess what you want is to return a Vector with the value type being the type returned by the per-component operation, which is not necessarily the type of the scalar.
For example:
Vector<int> * int -> Vector<int>
Vector<int> * double -> Vector<double>
Vector<double> * int -> Vector<double>
Vector<char> * float -> Vector<float>
etc.
For this, you should define the two input types separately, let's say T1
and T2
(and one or two of the operands is a Vector
of it). You don't want to simply use the scalar type (for vector * scalar, or scalar * vector operation) as the result, otherwise it might be converted (see my 3rd example: The result would then be Vector<int>
.
The above can be done using decltype
to find the third (result) type.
For simplicity, define the operator as a non-member:
template<typename T1, typename T2, typename T3 = decltype(std::declval<T1>() * std::declval<T2>())>
Vector<T3> operator*(const Vector<T1>& lhs, const T2 & scalar) // inner product
{
Vector<T3> result;
//...
return result;
}
The interesting part is
T3 = decltype(std::declval<T1>() * std::declval<T2>())
Here, we find the type T3
using the other two types T1
and T2
. First, we construct two values with unimportant value (the std::declval
function is a helper function returning the type given as a template parameter). Then we multiply those values, but again the result is unimportant; we're only interested in the type. That's what the third part does: decltype
gives you the type of the expression (without evaluating it).
The other operators can be implemented analogous.
In order to make those operators friends, you need the syntax
template<...> friend ...
as seen in Danvil's answer.
Upvotes: 1
Reputation: 22991
The friend declaration should look like this:
template<class S>
class Vector
{
private:
std::vector<S> base;
public:
template<class T> friend T operator*(const Vector<T>& lhs, const Vector<T>& rhs); // inner product
template<class T> friend Vector<T> operator*(const T scalar); // vector*scalar
template<class T> friend Vector<T> operator*(const T scalar, const Vector<T>& rhs); // scalar*vector
};
And the rest of the code like this:
template<class T>
T operator*(const Vector<T>& lhs, const Vector<T>& rhs) // inner product
{
assert( lhs.base.size() == rhs.base.size() );
Vector<T> result;
result.base.reserve(lhs.base.size());
std::transform( lhs.base.begin(), lhs.base.end(), rhs.base.begin(), std::back_inserter(result.base), std::multiplies<T>() );
return result;
}
template<class T>
Vector<T> operator*(const Vector<T>& lhs, const T scalar) // vector*scalar
{
return scalar*lhs;
}
template<class T>
Vector<T> operator*(const T scalar, const Vector<T>& rhs) // scalar*vector
{
Vector<T> result;
result.base.reserve(rhs.base.size());
std::transform( rhs.base.begin(), rhs.base.end(), std::back_inserter(result.base), std::bind1st(std::multiplies<T>(), scalar) );
return result;
}
Upvotes: 1
Reputation: 3939
You can add more template parameters as such:
template<class T, class S>
Vector<S> Vector<T>::operator*(const S scalar)
and make sure you use S
for the scalar type and T
for the vector element type in the correct places in the function body.
Upvotes: 0