2015-09-08 4 views
6

Ich versuche, einen has_ostream_operator<T> SFINAE-Test zu definieren, um zu überprüfen, ob ich einen bestimmten Typ cout kann. Ich habe es funktioniert, aber nur wenn ich in meiner Definition von has_ostream_operatoroperator<< als eine Methode statt als Infix-Operator aufrufen. Mit anderen Worten: das funktioniert:Warum muss ich Operator << als Methode für SFINAE aufrufen, um mit void_t zu arbeiten?

decltype(std::declval<std::ostream>().operator<<(std::declval<T>()))>

Dies gilt nicht:

decltype(std::declval<std::ostream>() << std::declval<T>())>

Testfall unten (kann auch bei http://coliru.stacked-crooked.com/a/d257d9d6e0f3f6d9 sehen). Beachten Sie, dass ich eine Definition von void_t aufgenommen habe, da ich nur in C++ 14 bin.

#include <iostream> 

namespace std { 

    template<class...> 
    using void_t = void; 

} 

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>().operator<<(std::declval<T>()))>> 
    : std::true_type {}; 

struct Foo {}; 

template<class X> 
    void print(
     const X& x, 
     std::enable_if_t<has_ostream_operator<X>::value>* = 0) 
{ 
    std::cout << x; 
} 

template<class X> 
    void print(
     const X&, 
     std::enable_if_t<!has_ostream_operator<X>::value>* = 0) 
{ 
    std::cout << "(no ostream operator<< implementation)"; 
} 

int main() 
{ 
    print(3); // works fine 
    print(Foo()); // this errors when using infix operator version 
    return 0; 
} 
+0

Diese Definition von 'void_t' induziert nicht definiertes Verhalten ([namespace.std]/1). – Columbo

+0

Sind Sie sicher, dass Sie alle diese Probleme durchgehen müssen, um einen Fehler bei der Kompilierung in einen Laufzeitfehler zu konvertieren? Ich bin sehr neugierig auf den tatsächlichen Anwendungsfall. Ist Ihr Ziel wirklich in der Lage zu sein, 'print (x)' für jedes 'x' zu schreiben und diese Laufzeitnachricht immer dann angezeigt zu bekommen, wenn' x' nicht druckbar ist? – JorenHeit

Antwort

9

Ich gehe davon aus Ihrer "Infix" Version verwenden diesen Ausdruck:

std::declval<std::ostream>() << std::declval<T>() 

Der Grund, der für Foo passt, ist, weil der erste Teil declval<ostream>() einen Rvalue des Typs ostream&& produziert. Dies entspricht ein Nichtmitglied operator<<:

template< class CharT, class Traits, class T > 
basic_ostream< CharT, Traits >& operator<<(basic_ostream<CharT,Traits>&& os, 
              const T& value); 

die einfach vorwärts entlang den Anruf überlasteten:

ruft den entsprechenden Ausgabe-Operator, da eine rvalue Bezugnahme auf einen Ausgangsstromobjekt (äquivalent zu os << value).

Sie sollten stattdessen dafür direkt überprüfen. Alle Überlastungen nehmen eine ostream von lvalue Referenz, so dass Sie testen sollen:

std::declval<std::ostream&>() << std::declval<T>() 
6

Sie benötigen

std::declval<std::ostream&>() << std::declval<T>() 
//      ^

std::declval<std::ostream>() ein R-Wert ist; Sie treffen die Catch-All operator<< Überladung für Rvalue-Streams.

+1

Würdest du die Tatsache in Betracht ziehen, dass es einen solchen Catch-All-Standard-Defekt gibt (a la 'std :: function', der ursprünglich mit irgendetwas konstruierbar ist)? – Barry

+0

@Barry Ich vermute, dass es nicht wirklich ein Problem vor 'void_t' war, aber heutzutage .... könnte ein LWG-Problem wert sein. –

+0

Zuerst gab es C++ 11, dann gab es C++ void_t. Bald wird C++ is_detected sein. – Barry

4

Wenn Sie die Infix-Notation verwenden, wird der rvalue-Stream-Inserter gefunden, da declval rvalues ​​per se zurückgibt; [Ostream.rvalue]:

template <class charT, class traits, class T> 
basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x); 

Diese Überlastung nimmt derzeit alle Argumente für x. Ich habe LWG #2534 eingereicht, das, wenn es entsprechend aufgelöst wird, Ihren ursprünglichen Code wie erwartet funktionieren lässt.

Eine vorübergehende Lösung ist declval Rückkehr einer L-Wert Referenz, also passen Sie die Vorlage Argument zu machen:

std::declval<std::ostream&>() << std::declval<T>() 
Verwandte Themen