2015-12-16 6 views
5

Używam C++03 method to detect the presence of a function at compile time. Muszę używać tej metody, a nie metody void_t, mimo że używam C++ 14, ponieważ muszę obsługiwać GCC 4.9, i to jest błędem podczas używania metody void_t (dziwnie tylko GCC 4.9 Ubuntu 14 ma ten problem, a nie Fedorę , ale zostało to naprawione w GCC5 + AFAICT).Wykryj operatora w czasie kompilacji bez domyślnych konwersji

W szczególności sprawdzam obecność operator<<(std::ostream&, const T&), dzięki czemu mogę mieć ładną funkcję drukowania, która ma dowolny typ. Gdy funkcja zostanie wywołana, otrzymasz regularne wyjście ostream, jeśli typ go obsługuje, i otrzymasz komunikat awaryjny o braku implementacji, gdy operator nie jest zdefiniowany. Kod na dole.

Do tej pory działało to dobrze, dopóki nie natrafiłem na typ zdefiniowany przez bibliotekę innej firmy, której nie mogę zmienić. Typ ma niejawnych operatorów konwersji zarówno do bool i float. Oznacza to, że gdy sprawdzanie SFINAE jest wykonywane, aby sprawdzić, czy s << t jest poprawny, pojawia się błąd kompilatora, ponieważ s << t jest niejednoznaczny. W tym przypadku wolałbym, aby po prostu zgłosił, że nie ma takiej implementacji jak normalna, zamiast próbować wybrać niejawną konwersję. Czy istnieje sposób na zmianę testu SFINAE, aby było to możliwe? Sprawdziłem i metoda void_t z GCC5 wydaje się robić to, co chcę (skomentowałem w kodzie poniżej), ale nie mogę go jeszcze użyć z wyżej wymienionych powodów.

przypadek testowy:

#include <iostream> 
#include <typeinfo> 
#include <type_traits> 

namespace detail { 

    namespace has_ostream_operator_impl { 
     typedef char no; 
     typedef char yes[2]; 

     struct any_t { 
      template<typename T> any_t(T const&); 
     }; 

     no operator<<(std::ostream const&, any_t const&); 

     yes& test(std::ostream&); 
     no test(no); 

     template<typename T> 
     struct has_ostream_operator { 
      static std::ostream &s; 
      static T const &t; 
      // compiler complains that test(s << t) is ambiguous 
      // for Foo 
      static bool const value = sizeof(test(s << T(t))) == sizeof(yes); 
     }; 
    } 

    template<typename T> 
    struct has_ostream_operator : 
     has_ostream_operator_impl::has_ostream_operator<T> { 
    }; 

    // template<class, class = std::void_t<>> 
    //  struct has_ostream_operator : std::false_type {}; 

    // template<class T> 
    // struct has_ostream_operator< 
    //  T, 
    //  std::void_t< 
    //   decltype(std::declval<std::ostream&>() << std::declval<const T&>())>> 
    //  : std::true_type {}; 

} 

template<class X> 
std::enable_if_t< 
    detail::has_ostream_operator<X>::value 
    && !std::is_pointer<X>::value> 
prettyPrint(std::ostream& o, const X& x) 
{ 
    o << x; 
} 

template<class X> 
std::enable_if_t< 
    !detail::has_ostream_operator<X>::value 
    && !std::is_pointer<X>::value> 
prettyPrint(std::ostream& o, const X& x) 
{ 
    o << typeid(x).name() 
     << " (no ostream operator<< implementation)"; 
} 

template<class X> 
void prettyPrint(std::ostream& o, const X* x) 
{ 
    o << "*{"; 
    if(x) { 
     prettyPrint(o, *x); 
    } else { 
     o << "NULL"; 
    } 
    o << "}"; 
} 

struct Foo { 
    operator float() const { 
     return 0; 
    } 

    operator bool() const { 
     return false; 
    } 
}; 

struct Bar {}; 

int main() 
{ 
    Bar x; 
    Foo y; 
    prettyPrint(std::cout, 6); // works fine 
    std::cout << std::endl; 

    prettyPrint(std::cout, Bar()); // works fine 
    std::cout << std::endl; 

    prettyPrint(std::cout, x); // works fine 
    std::cout << std::endl; 

    prettyPrint(std::cout, &x); // works fine 
    std::cout << std::endl; 

// prettyPrint(std::cout, y); // compiler error 
    std::cout << std::endl; 

    return 0; 
} 
+1

Dla GCC <= 4,9 użyj [poniższego rozwiązania] (http://coliru.stacked-crooked.com/a/aafcc71f98311f93) –

+0

Problem "void_t'/CWG1558 jest trywialny do obejścia. –

+0

@ Piotrotrkotnicki oh, nie zdawałem sobie z tego sprawy. Zrób odpowiedź poniżej i zaakceptuję. Chociaż chciałbym wiedzieć, * dlaczego * wersja void_t nie ma tego problemu. Nadal wywołuje operatora ... jest typem zwracania danych z jakiegoś powodu z jakiegoś powodu, który nie jest domyślnie wymienialny? –

Odpowiedz

3

Cóż, nie trzeba używać void_t (co jest cukier składni, w każdym razie). Bog standard wyrażenie SFINAE jest obsługiwane przez GCC 4.9:

template <typename, typename = void> 
struct has_ostream_operator : std::false_type {}; 

template <typename T> 
struct has_ostream_operator<T, decltype(void(std::declval<std::ostream&>() << std::declval<const T&>()))> 
    : std::true_type {}; 

działa dobrze na Wandbox's GCC 4.9.

+0

Wygląda to identycznie jak część void_t mojego fragmentu, która nie działa w GCC 4.9 systemu Ubuntu. Jak już mówiłem, działa na Fedorze, więc Ubuntu musiał to załatać, czy coś. –

+0

@JosephGarvin Testowałeś to? Trudno uwierzyć, że GCC 4.9 na jakiejkolwiek dystrybucji nie obsługuje tak podstawowej SFINAE; Błąd wydaje się być bezpośrednio związany z użyciem samego 'void_t'. – Columbo

+0

Tak, przetestowałem to. Sam się osłupiałem. Błąd, który otrzymujesz, ma związek z przeciążeniem ostream i niejednoznacznością (ale pomimo tego, że ma to coś wspólnego, * nie * jest taki sam jak w moim wpisie). Więc nie sądzę, że jest to problem void_t, ale pewna różnica w programie iostream. –

Powiązane problemy