Reputation: 11
I am trying to build a templated vector class that can be based on any arithmetic type and do math with them. So a Vec3<T> + Vec3<U> should be permitted. Here is my declaration:
template <typename T>
class Vec3 {
private:
T _x, _y, _z;
public:
static_assert(std::is_arithmetic_v<T>, "Type must be arithmetic");
Vec3(T x, T y, T z);
~Vec3() = default;
template <typename U, typename V>
friend Vec3<T> operator+(Vec3<U> lhs, const Vec3<V>& rhs);
};
I wrote two definitions for this function (same hpp file but further down), one if all input types are the same the output type should be the same. But if one of them is different the output depends: int+double-> double, double+int -> double, long+int -> long, ... you get the idea.
template <typename T>
Vec3<T> operator+(Vec3<T> lhs, const Vec3<T>& rhs) {
return Vec3<T>(T x, T y, T z);
}
template <>
Vec3<double> operator+(Vec3<int> lhs, const Vec3<double>& rhs) {
return Vec3<double>(double x, double y, double z);
}
template <>
Vec3<double> operator+(Vec3<double> lhs, const Vec3<int>& rhs) {
return Vec3<double>(double x, double y, double z);
}
...
I thought I would overload the operator+ for every combination of permissible types. However I'm getting an error:
error: template-id 'operator+><' for 'Color3<double> operator+(Color3<int>, const Color3<double>&)' does not match any template declaration
What's the proper way to go about setting up any number of possible combinations of types? Thanks.
Upvotes: 0
Views: 116
Reputation: 118097
If you want any two Vec3
based entities to be able to use operator+
you can add a friend
that deduces the proper return type: Vec3<decltype(U{} + V{})>
:
template <typename T>
class Vec3 {
public:
static_assert(std::is_arithmetic_v<T>, "Type must be arithmetic");
Vec3(T x, T y, T z) : _x(x), _y(y), _z(z) {}
~Vec3() = default;
// matches any two Vec3<>s
template <class U, class V>
friend Vec3<decltype(U{} + V{})> operator+(const Vec3<U>& lhs,
const Vec3<V>& rhs);
private:
T _x, _y, _z;
};
template <class U, class V>
Vec3<decltype(U{} + V{})> operator+(const Vec3<U>& lhs, const Vec3<V>& rhs) {
return {lhs._x + rhs._x, lhs._y + rhs._y, lhs._z + rhs._z};
}
Upvotes: 1
Reputation: 67872
This isn't a direct answer to the question as posed, but a demo of how easy this is to write if you organize your data into something compatible with ranges:
#include <array>
#include <functional>
#include <iostream>
#include <ranges>
#include <utility>
template <typename F, typename... R>
auto vsum(F&& f, R&& ...ranges)
{
return std::views::zip_transform(
std::forward<F>(f), std::forward<R>(ranges)...
);
}
int main()
{
std::array<int, 3> u{ 1, 2, 3 };
std::array<double, 3> v{ 6.1, 6.2, 6.3 };
for (auto e: vsum(std::plus{}, u, v))
std::cout << e << '\n';
}
You can add type constraints if you want, but vsum(std::plus{}, ...)
works automatically for everything with a suitable operator+
.
I made it variadic because it was actually less typing for a quick demo, but there's no problem fixing it to two range parameters. Obviously it also generalizes with no effort to any number of dimensions.
Upvotes: 1