Ambiguous operator << choice

I have a code that is very simplified, looks something like this:

#include <iostream>
#include <type_traits>

namespace X {
    struct Foo {int x;};
    struct Bar {int x;};

    template <typename T , typename = typename std::enable_if<
                                                              std::is_same<decltype(T::x),int>::value
                                                             >::type>
    std::ostream & operator<<(std::ostream & os, const T&) {
        return os;
    }
}

namespace Y {
    struct Faa : X::Foo {int y;};
    struct Baz {int x; int y;};

    template <typename T , typename = typename std::enable_if<
                                                              std::is_same<decltype(T::x),int>::value && 
                                                              std::is_same<decltype(T::y),int>::value
                                                             >::type>
    std::ostream & operator<<(std::ostream & os, const T&) {
        return os;
    }
}


int main() {
    // Everything is ok
    X::Foo x;
    std::cout << x;

    Y::Baz k;
    std::cout << k;

    // Problems..
    Y::Faa y;

    // std::cout << y; // <--operator is ambiguous
    Y::operator<<(std::cout, y);

    return 0;
}

Is there a way to avoid the ambiguous operator for Y::Faaand specify manually Y::operator<<? If not, why?

+4
source share
1 answer

Two functions have a conflict because the conditions of their arguments have a nonempty intersection (in fact, the 1st replaces the 2nd). Function overloading only works if the signatures are different. So, to solve this, we have 2 options:

, ( y, && !sfinae_has_member_y<T>::value 1- enable_if)

template<typename T>
struct sfinae_has_member_y {
    static int has(...);
    template<typename U = T, typename = decltype(U::y)>
    static char has(const U& value);
    enum { value = sizeof(char) == sizeof(has(std::declval<T>())) };
};

++, , /. bool int, :

template<typename T, bool>
struct Outputter {
};
template<typename T>
struct Outputter<T, false> {
    static std::ostream & output(std::ostream & os, const T&) {
        os << "x";
        return os;
    }
};
template<typename T>
struct Outputter<T, true> {
    static std::ostream & output(std::ostream & os, const T&) {
        os << "y";
        return os;
    }
};

template<typename T, typename = std::enable_if_t<std::is_same<decltype(T::x), int>::value>>
std::ostream & operator<<(std::ostream & os, const T& a) {
    return Outputter<T, sfinae_has_member_y<T>::value>::output(os, a);
}
+3

All Articles