Reputation: 26374
I have a Vector
class which represents a 2D vector. It is templated to allow any numerical type to be used for the x and y components. As an example, one of the arithmetic operators I overload is *
for multiplying a vector with a scalar:
template <typename T, typename U>
inline const Vector<T> operator*(const Vector<T>& vector, U scalar) {
return Vector<T>(vector.x * scalar, vector.y * scalar);
}
(I also have a function with the parameters in the opposite order to allow scalar * Vector
in addition to Vector * scalar
).
As you can see, I use <T, U>
instead of simply <T>
so that the scalar doesn't have to be the same type as the Vector. When I didn't do this, surprisingly Vector<double> * int
wouldn't compile (I thought the int would automatically widen).
In any case, I don't simply want to return a Vector<T>
. I want to mimic the built-in types and return whichever has higher precision, T
or U
. So for example, Vector<int> * double => Vector<double>
while Vector<double> * short => Vector<double>
.
Is this possible?
Upvotes: 4
Views: 1374
Reputation: 88711
There are two solutions to this. In Pre C++11 you can write a template like:
template <typename T, typename U>
struct WhatWillItBe {
typedef T result_t;
};
template <typename T>
struct WhatWillItBe<T, double> {
typedef double result_t;
};
// ... lots more
etc. and write a lot of specialisations, then you can use that to look up the return type, e.g.:
template <typename T, typename U>
inline const Vector<typename WhatWillItBe<T,U>::result_t> operator*(const Vector<T>& vector, U scalar) {
return Vector<typename WhatWillItBe<T,U>::result_t>(vector.x * scalar, vector.y * scalar);
}
Alternatively C++11 makes this straightforward, you can write auto
for the return type and use ->
to specify the return type after the rest of the function:
template <typename T, typename U>
inline auto operator*(const Vector<T>& vector, U scalar) -> Vector<decltype(vector.x*scalar)> {
return Vector<decltype(vector.x*scalar)>(vector.x * scalar, vector.y * scalar);
}
Which allows you to use decltype
for the return type of a function, setting it based on what would happen for promotion naturally with vector.x * scalar
.
Upvotes: 4
Reputation: 477040
You can use common_type
or decltype
to cook up something that gives you the resulting type; and then you have to create the actual vector:
template <typename A, typename B>
std::vector<typename std::common_type<A, B>::type>
operator*(std::vector<A> const & v, B const & x)
{
std::vector<typename std::common_type<A, B>::type> res;
res.reserve(v.size());
for (A a : v) res.push_back(a * x);
return res;
}
Using decltype
, you can get at the result type via:
decltype(std::declval<A>() * std::declval<B>())
For both std::common_type
and std::declval
you need to #include <type_traits>
.
With delayed return types (auto
and ->
) you can use decltype
directly on the function arguments, but using std::declval
feels a bit more hygienic, since it doesn't require you to furnish an actual instance of your type (and thus it is applicable even in situations where this isn't possible).
Upvotes: 6