problems with overload resolution and operator<

2019-08-10 06:41发布

问题:

I'm writing a library and it should stringify objects.

I've chosen to support operator<<(ostream&....

Another thing is that my library should provide default stringification of types that don't have operator<<(ostream&... in the {?} form.

The problem is with templated types like vector<> - I don't want the user to write 2 overloads for vector<int> and vector<float> - but I cannot get it to work.

Here is the code:

#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> // note the negation here compared to the one above
typename enable_if< ! has_insertion_operator<T>::value, string>::type stringify(const T&) {
    return "{?}";
}

// USER CODE:

struct myType {};

ostream& operator<<(ostream& s, const myType&) { s << "myType"; return s; }

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

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

    return 0;
}

myType and the integer both get stringified but for the vector<int> I get the default {?}.

I need help on this - it is a showstopper for me. I need user-provided operator<<(ostream&... overloads to work out-of-the-box without modification - and all this in C++98.

回答1:

To match the vector you need to provide better candidates for your stringize functions

For C++11 this would be:

template <template <typename...> class V, typename... Args>
typename enable_if< !has_insertion_operator<V<Args...>>::value, string>::type stringify(const V<Args...>&) {
    return "template {?}";
}
// also add enable_if for yes with code identical to the one in template<typename T>

Since you need C++98, I'm afraid you'll need to provide a number of template overloads for 1 to N (say 5 or 10) args. Like this.

template <template <typename> class V, typename T0>
...
template <template <typename, typename> class V, typename T0, typename T1>
...
/// etc

For example vector in your case has 2 template arguments, and will match the second pair of yes/no functions



回答2:

Turns out all I had to do was remove the namespace with the implementation of the insertion operator trait so the fallback operator<<(ostream&... ends up in global scope - explained here.