2014-09-23 105 views
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<<?如果不是,为什么?

+0

通过'enable_if'所施加的限制似乎很微弱(种类太多被允许)。你可以改进它们吗?通过使用类型特质? – dyp 2014-09-23 14:27:16

+0

在我的实际代码中,我做了一个检查各种成员方法存在的实际特征,但这个想法是相同的。尽管如此,我认为这还不算什么问题。 – Svalorzen 2014-09-23 14:39:31

+0

我宁愿想像某种允许类型的列表;可以让你“确定”哪些类型是(作为)命名空间的直接成员。从这些,你可以选择那些名为'x'的成员。 – dyp 2014-09-23 14:43:37

回答

3

两个函数有冲突,因为它们的参数条件有非空的交集(实际上,1st取代2nd)。只有签名不同时,函数重载才起作用。因此,要解决这个我们有2种选择:

变化条件使得它们具有空交集(手动禁止通过添加&& !sfinae_has_member_y<T>::value状态到第一enable_if具有y字段)

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++支持参数重叠的功能,如结构/类模板专业化。如果您有int替换bool,其他领域可能太补充说:

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