2016-05-10 12 views
3

der folgende Code Gegeben:Probleme mit Überladungsauflösung und Operator << für Templat-Typen - Teil 2

#include <string> 
#include <type_traits> 
#include <sstream> 
#include <vector> 
#include <iostream> 
using namespace std; 

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

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

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

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

    template <typename T> 
    struct has_insertion_operator { 
     static ostream& s; 
     static T const&  t; 
     static bool const value = sizeof(test(s << t)) == sizeof(yes); 
    }; 
} 

template <typename T> 
struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator<T> {}; 

template <class T> 
typename enable_if<has_insertion_operator<T>::value, string>::type stringify(const T& in) { 
    stringstream stream; 
    stream << in; 
    return stream.str(); 
} 

template <class T> 
typename enable_if< ! has_insertion_operator<T>::value, string>::type stringify(const T&) { 
    return "{?}"; 
} 

// ======= OVERLOADS PROVIDED BY THE USER ======= 

template<typename T, typename T2> 
struct myType { T data; T2 op; }; 

template<typename T, typename T2> 
ostream& operator<<(ostream& s, const myType<T, T2>&) { s << "myType"; return s; } 

template<typename T> 
ostream& operator<<(ostream& s, const vector<T>&) { s << "vector<T>"; return s; } 

template<typename T, typename A> 
ostream& operator<<(ostream& s, const vector<T, A>&) { s << "vector<T, A>"; return s; } 

int main() { 
    myType<int, float> a; cout << stringify(a) << endl; // prints "myType" 
          cout << stringify(6) << endl; // prints "6" 
    vector<int> v(5);  cout << stringify(v) << endl; // prints "{?}" 

    return 0; 
} 

Warum wird die Vorlage myType<> Zeichenfolge erhalten, aber der Templat-vector<> Typ nicht?

Für den vector<> Typen erhalte ich den Standard {?} stringification aber ich mag eindeutig eine der Überlastung am Boden genannt zu werden - genau wie mit myType<>

EDIT:

Die eigentliche Frage ist, Warum ist has_insertion_operator<vector<int>> falsch?

Ich brauche dies auch in C++ 98

Und die operator<< Überlastungen sollten nach stringify() geliefert werden - wie bei myType<>

+0

Es ist offensichtlich, warum der Vektor nicht beschriftet ist. Die Frage kann vereinfacht werden zu "Warum ist' has_insertion_operator > 'false?" – user2079303

+0

@ user2079303: Was ist "offensichtlich" darüber? Es gibt einen "ostream & operator" (ostream & s, const vector &) genau dort. –

+1

@LightnessRacesinOrbit gut eigentlich hat er Recht - es ist die Eigenschaft der Schuld - es treibt die '' 'enable_if <>' '' und die SFINAE – onqtam

Antwort

1

ich denke, das Problem mit dem Look-up ist, und ich werde mein Verständnis geben.

Wenn Sie die operator<< in Ihrem has... struct Argument abhängige Lookup-Kicks in und seit myType lebt im gleichen Namensraum nennen, die die operator<< überlastet es gegründet wird und Sie erhalten eine richtige Zeichenfolge.Aber wenn Sie versuchen, eine vector Ausgabe auszugeben, versucht es, nach dem überladenen operator<< durch die gleichen argumentabhängigen Nachschlag-Regeln zu suchen, und es schlägt fehl, da es keine überladenen Operatoren in std-Namespace gibt. So fällt es zurück zum uneingeschränkten Suche, die aus dem Namensraum beginnt dort, wo der Anruf erfolgt, daher findet es Ihre Stub operator<<

Also, um es zu beheben Sie operator<< Überlastungen auf den std Namespace platziert haben könnte (die durch verboten der Standard) oder entfernen Sie Ihren Namensraum - es wird den gleichen Effekt geben.

Sie müssen jedoch nicht alles aus dem privaten Namespace entfernen. Es ist genug, so etwas wie dies im globalen Namespace zu tun:

typedef char no; 
typedef char yes[2]; 
template<typename T> 
no operator<<(ostream const&, T const&); 

Oder, wenn es möglich ist, es besser ist, die Bibliothek der Benutzer ihre Überlastungen in den gleichen Namensraum gebracht, wo ihre Klassen leben. Es funktioniert jedoch nicht mit std Mitgliedern.

1

Es gibt zwei Probleme hier.

Die erste ist, dass etwas in der has_insertion_operator<> Prädikat ist nicht in Ordnung.

ich es mit dieser ersetzt ...

template<class T> 
struct has_insertion_operator 
{ 
    template<class U> 
    static auto test(U*p) -> decltype((std::declval<std::ostream>() << (*p)), void(), std::true_type()); 

    template<class U> 
    static auto test(...) -> std::false_type; 

    static constexpr bool value = decltype(test<T>(0))::value; 
}; 

..., das dieses Problem behoben, und markiert die nächste (was für Sie vielleicht noch schlimmer ist):

./stringify.cpp:73:12: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup 
    stream << in; 
     ^
./stringify.cpp:100:37: note: in instantiation of function template specialization 'stringify<std::__1::vector<int, std::__1::allocator<int> > >' requested here 
    vector<int> v(5);  cout << stringify(v) << endl; // prints "{?}" 
            ^
./stringify.cpp:91:10: note: 'operator<<' should be declared prior to the call site 
ostream& operator<<(ostream& s, const vector<T>&) { s << "vector<T>"; return s; } 
     ^
1 error generated. 

Diese ist, weil die Template-Funktion operator<< <std::vector...> referenziert wird, bevor sie definiert ist.

Sobald Sie die Definition über die Definition von stringify verschieben, funktioniert alles.

Finale, note:

operator<< für std::vector Überlastung ist eine wirklich schlechte Idee. Es wird Ihnen später alle Arten von Kopfschmerzen verursachen.

Aber ich bin mit C++ 98 und ich mag Benutzer in der Lage sein, ihre eigenen Überlastungen [Spezialisierungen]

Ok dann zu liefern, machen sie es die einfache (und richtig) Art und Weise , die für alle Varianten von c arbeiten ++ und wird keine Kopfschmerzen aufgrund des Eindringens des std-Namespace mit illegalen Überlastungen verursachen:

#include <string> 
#include <sstream> 
#include <vector> 
#include <iostream> 


// define a template class emitter which by default simply calls operator<< 
template<class T> 
struct emitter 
{ 
    emitter(const T& v) : _v(v) {} 
    std::ostream& operator()(std::ostream& os) const { 
     return os << _v; 
    } 
    const T& _v; 
}; 

// emitter<>'s are streamable 
template<class T> 
std::ostream& operator<<(std::ostream& os, const emitter<T>& e) 
{ 
    return e(os); 
} 

// a factory function to generate the correct emitter 
template<class T> 
emitter<T> emit(const T& v) 
{ 
    return emitter<T>(v); 
} 

// write one version of stringify in terms of emit<>() 
template <class T> 
std::string stringify(const T& in) { 
    std::stringstream stream; 
    stream << emit(in); 
    return stream.str(); 
} 
// ======= OVERLOADS PROVIDED BY THE USER ======= 

template<typename T, typename T2> 
struct myType { T data; T2 op; }; 

// user must provide an emitter for each custom type 
template<typename T, typename T2> 
struct emitter<myType<T, T2> > 
{ 
    typedef myType<T, T2> value_type; 
    emitter(const value_type& v) : _v(v) {} 

    std::ostream& operator()(std::ostream& os) const 
    { 
     return os << "myType"; 
    } 
private: 
    const value_type& _v; 
}; 

// and for any std:: templates he wants to support 
template<class V, class A> 
struct emitter<std::vector<V, A> > 
{ 
    typedef std::vector<V, A> value_type; 
    emitter(const value_type& v) : _v(v) {} 

    std::ostream& operator()(std::ostream& os) const 
    { 
     return os << "vector<T, A>"; 
    } 
private: 
    const value_type& _v; 
}; 

// test 
int main() { 
    myType<int, float> a; std::cout << stringify(a) << std::endl; // prints "myType" 
          std::cout << stringify(6) << std::endl; // prints "6" 
    std::vector<int> v(5); std::cout << stringify(v) << std::endl; // prints "vector<T, A>" 

    return 0; 
} 
+1

Ich brauche das in C++ 98 - und auch ich brauche die Überladungen unterhalb von '' 'stringify()' '' - wie es derzeit mit '' 'myType <>' '' – onqtam

+0

@onqtam auf das erste Element, denke ich, einige Debugging wird dich dorthin bringen. In der zweiten kann ich dir nicht helfen. Eine Vorlagenfunktion kann keine Vorlagenfunktion aufrufen, in der die allgemeine Form der Vorlage noch nicht existiert. Das ist eine Sprachbeschränkung. –

+0

"* Eine Template-Funktion kann keine Template-Funktion aufrufen, wo die allgemeine Form der Vorlage noch nicht existiert" * aber OP fragt nach der Diskrepanz zwischen 'vector' und' myType' –