I have 2 different class templates in the same namespace, xy and rgba. Both of them overload operators == and !=. When I compile, I get the error that the overload has already been defined. Is it because both those classes are in the same namespace? If so, is there a trick to avoid the clash? I tried different labels for the templates, it gave me the same result.
template <typename A>
class xy
{
public:
A x, y;
xy() :x(0), y(0) {}
xy(A x, A y) :x(x), y(y) {}
template<typename B>
xy& operator = (const B& v) {
x = A(v.x), y = A(v.y); return*this;
}
template<typename B, typename C>
friend bool operator == (const B& a, const C& v) {
return a.x == v.x && a.y == v.y;
}
template<typename B, typename C>
friend bool operator != (const B& a, const C& v) {
return a.x != v.x || a.y != v.y;
}
template<typename B>
operator B() const {
return B(x, y);
}
};
class rgba
{
public:
int r, g, b, a;
rgba() :r(255), g(255), b(255), a(255) {}
rgba(int r, int g, int b, int a) :r(r), g(g), b(b), a(a) {}
template<typename B>
rgba& operator = (const B& v) {
r = A(v.r), g = A(v.g), r = A(v.b), a = A(v.a); return*this;
}
template<typename B, typename C>
friend bool operator == (const B& a, const C& v) { // <- already defined ?
return a.r == v.r && a.g == v.g && a.b == v.b && a.a == v.a;
}
template<typename B, typename C>
friend bool operator != (const B& a, const C& v) { // <- already defined ?
return a.r != v.r || a.g != v.g || a.b != v.b || a.a != v.a;
}
template<typename B>
operator B() const {
return B(r, g, b, a);
}
};
There are many good points in the comment, so I will only provide a solution to your compilation problem.
You need some type_traits
and if constexpr
. If you don't have c++17
you can do it with SFINAE
.
#include <iostream>
#include <type_traits>
template <class T, class U = void>
struct has_xy : std::false_type{};
template <class T>
struct has_xy<T, std::void_t<
decltype(std::declval<T>().x),
decltype(std::declval<T>().y)
>
>: std::true_type{};
template <class T, class U = void>
struct has_rgba : std::false_type{};
template <class T>
struct has_rgba<T, std::void_t<
decltype(std::declval<T>().r),
decltype(std::declval<T>().g),
decltype(std::declval<T>().b),
decltype(std::declval<T>().a)
>
>: std::true_type{};
template <typename A>
class xy
{
public:
A x, y;
xy() :x(0), y(0) {}
xy(A x, A y) :x(x), y(y) {}
template<typename B>
xy& operator = (const B& v) {
x = A(v.x), y = A(v.y); return*this;
}
template<typename B, typename C>
friend bool operator == (const B& a, const C& v);
template<typename B, typename C>
friend bool operator != (const B& a, const C& v);
template<typename B>
operator B() const {
return B(x, y);
}
};
class rgba
{
public:
int r, g, b, a;
rgba() :r(255), g(255), b(255), a(255) {}
rgba(int r, int g, int b, int a) :r(r), g(g), b(b), a(a) {}
template<typename B>
rgba& operator = (const B& v) {
r = A(v.r), g = A(v.g), r = A(v.b), a = A(v.a); return*this;
}
template<typename B, typename C>
friend bool operator == (const B& a, const C& v);
template<typename B, typename C>
friend bool operator != (const B& a, const C& v);
template<typename B>
operator B() const {
return B(r, g, b, a);
}
};
template<typename B, typename C>
bool operator == (const B& a, const C& v)
{
if constexpr ( has_xy<B>::value and has_xy<C>::value )
{
return a.x == v.x && a.y == v.y;
}
else if constexpr ( has_rgba<B>::value and has_rgba<C>::value )
{
return a.r == v.r && a.g == v.g && a.b == v.b && a.a == v.a;
}
else
{
return false; // or throw, or don't compile do as you want
}
}
template<typename B, typename C>
bool operator != (const B& a, const C& v)
{
return not operator==(a,v);
}
int main()
{
xy<float> x1,x2;
rgba r1,r2;
if ( x1 == x2 ) { std::cout << " x1 == x2 " << std::endl; }
if ( x1 != x2 ) { std::cout << " x1 != x2 " << std::endl; }
if ( r1 == r2 ) { std::cout << " r1 == r2 " << std::endl; }
if ( r1 != r2 ) { std::cout << " r1 != r2 " << std::endl; }
if ( x1 == r2 ) { std::cout << " x1 == r2 " << std::endl; }
if ( x1 != r2 ) { std::cout << " x1 != r2 " << std::endl; }
}
Live demo : wandbox
Both class are friend with the same function, and the function is only declare once outside of the classes.
Then inside the operator, we select, at compile time, if we can compare en x/y
or on r/g/b/a