2014-09-23 2 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

+0

В моем фактическом коде я сделал фактическую черту, которая проверяет существование различных методов-членов, но идея такая же. Я не думаю, что это все еще проблема. – Svalorzen

+0

Я скорее подумал о чем-то вроде списка разрешенных типов; что позволяет «определять», какие типы (предназначены как) непосредственные члены пространства имен. Из этого вы можете выбрать те, у которых есть член с именем 'x'. – dyp

ответ

3

Две функции имеют конфликт, поскольку условия их аргументов имеют непустое пересечение (на самом деле, 1-й заменяет второй). Перегрузка функций работает только в том случае, если подписи различны. Таким образом, чтобы решить эту проблему, мы имеем 2 варианта:

Изменить условия так, что они имеют пустое пересечение (вручную запретить имеющие 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>())) }; 
}; 

или используйте другой C++ функция, которая поддерживает совпадение аргументов, например struct/class template специализация. Если заменить 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); 
} 
Смежные вопросы