4

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

#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<<?如果不是,为什么?

4

1 回答 1

3

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

更改条件以使它们具有空的交叉点(通过将条件添加到 1st手动禁止具有y字段)&& !sfinae_has_member_y<T>::valueenable_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。如果您替换boolint,也可能会添加其他字段:

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);
}
于 2014-11-29T23:14:10.160 回答