模糊运算符<<选择

时间:2014-09-23 14:21:25

标签: c++11 sfinae overload-resolution ambiguous-call

我有一些非常简化的代码看起来像这样:

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

有没有办法避免Y::Faa的模糊运算符并且必须手动指定Y::operator<<?如果没有,为什么?

1 个答案:

答案 0 :(得分:3)

两个函数存在冲突,因为它们的参数上的条件具有非空交集(实际上,第一个取代第二个)。仅当签名不同时,函数重载才起作用。因此,要解决这个问题,我们有两个选择:

更改条件,以便它们具有空交集(通过向{1} y添加&& !sfinae_has_member_y<T>::value条件,手动禁止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>())) };
};

或者使用支持重叠参数的其他C ++特性,例如 struct / class template specialization 。如果您将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);
}