2016-05-10 1 views
3

Étant donné le code suivant:problèmes avec la résolution de surcharge et l'opérateur << pour les types - Partie 2 templated

#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; 
} 

Pourquoi le modèle myType<> obtenir, mais le type de chaîne de caractères vector<> ne pas basé sur un modèle?

Pour le type vector<> je reçois la valeur par défaut {?} mise en chaîne mais je veux bien l'une des surcharges au fond d'être appelé - comme avec myType<>

EDIT:

La question réelle ici est Pourquoi le has_insertion_operator<vector<int>> est-il faux?

Je dois aussi cela en C++ 98

et doit être fourni après stringify() les operator<< Surcharges électriques - comme avec myType<>

+0

Il est évident que le vecteur n'est pas stringifié. La question peut être simplifiée en "Pourquoi est-ce que' has_insertion_operator > 'false?" – user2079303

+0

@ user2079303: Qu'est-ce qui est "évident" à ce sujet? Il y a un 'ostream & operator << (ostream & s, const vector &)' juste là. –

+1

@ LightnessRacesinOrbit bien en fait il a raison - c'est la faute du trait - il conduit le '' 'enable_if <>' '' et le SFINAE – onqtam

Répondre

1

Je pense que le problème est à la recherche et je fournirai ma compréhension.

Lorsque vous appelez la operator<< dans vos has... arguments struct coups de pied de consultation à charge et depuis myType habite dans le même espace de noms que le operator<< surchargées, il se fonde et vous obtenez une chaîne correcte.Mais lorsque vous essayez de sortir un vector il essaie de rechercher le operator<< surchargé par les mêmes règles de recherche dépendant de l'argument et ne parvient pas à le faire puisqu'il n'y a pas d'opérateurs surchargés dans l'espace de noms std. Ainsi, il revient à la recherche non qualifiée qui commence à partir de l'espace de noms où l'appel est fait, d'où il trouve votre talon operator<<

Ainsi, afin de le réparer vous pourriez avoir placé operator<< surcharge à l'espace de noms std (ce qui est interdit par la norme) ou supprimez votre espace de noms - cela donnera le même effet.

Cependant, vous n'avez pas besoin de tout ranger dans l'espace de noms privé. Il suffit de faire quelque chose comme ça dans l'espace de noms global:

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

Ou, s'il est possible, il est préférable d'exiger que les utilisateurs de la bibliothèque de mettre leurs dans les mêmes surcharges namespaces où leurs classes vivent. Cependant, cela ne fonctionnera pas avec les membres std.

1

Il y a deux problèmes ici.

La première est que quelque chose dans le prédicat has_insertion_operator<> est incorrect.

Je l'ai remplacé par celui-ci ...

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; 
}; 

... qui fixe ce problème, et a mis en évidence la prochaine (ce qui est peut-être plus grave pour vous):

./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. 

Cette est parce que la fonction de modèle operator<< <std::vector...> est référencée avant d'être définie. Une fois que vous déplacez la définition de cela au-dessus de la définition de stringify, tout fonctionne.

final, Note:

surcharge operator<< pour std::vector est une très mauvaise idée. Il vous causera toutes sortes de maux de tête plus tard.

Mais je suis en C++ 98 et je veux les utilisateurs d'être en mesure de fournir leurs propres spécialisations [] surcharge

Ok alors, faisons le chemin facile (et plus correct) , qui travaillera pour toutes les saveurs de C++ et va causer aucun maux de tête en raison de l'invasion de l'espace de noms std avec des surcharges illégales:

#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

J'ai besoin de ça en C++ 98 - et aussi j'ai besoin que les surcharges soient en dessous de '' 'stringify()' '' - comme cela fonctionne actuellement avec '' 'myType <>' '' – onqtam

+0

@onqtam sur le premier élément, je suppose que certains le débogage vous y amènera. Sur le second, je ne peux pas vous aider. Une fonction de modèle ne peut pas appeler une fonction de modèle où la forme générale du modèle n'existe pas encore. C'est une limitation de la langue. –

+0

"* Une fonction modèle ne peut pas invoquer une fonction modèle où la forme générale du modèle n'existe pas encore" * mais OP pose une question sur la différence entre 'vector' et' myType' –