名称空间中的模板朋友布尔重载冲突

template friend bool overload clash in namespace

我在同一个命名空间 xy 和 rgba 中有 2 个不同的 class 模板。它们都重载运算符 == 和 !=。当我编译时,我得到了重载已经被定义的错误。是因为这两个 classes 都在同一个命名空间中吗?如果是这样,是否有避免冲突的技巧?我为模板尝试了不同的标签,它给了我相同的结果。

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);
    }
};

评论中有很多好点,所以我只提供一个解决你编译问题的方法。

您需要一些 type_traitsif constexpr。如果你没有 c++17 你可以用 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; }
}

现场演示:wandbox

两个 class 是具有相同功能的朋友,并且该功能仅在 class 之外声明一次。

然后在运算符内部,我们select,在编译时,如果我们可以比较en x/y or on r/g/b/a