2014-09-23 114 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); 
}