Testing for the presence of the left shift operator

I am trying to find a working type property to determine if this type has a left shift operator overload for std::ostream(for example, compatible with std::coutor boost::lexical_cast). I have had success with boost::has_left_shift, except when the type is an STL container of POD or types std::string. I suspect this is due to specializations of STL types or the <function operator. What is the correct method for generic type determination with a valid left shift operator for std::ostream? If this is not possible, is there a separate method for detecting overload of the left shift operator on STL containers of POD or std :: string types?

The code below shows what I'm working with now and demonstrates how it is boost::has_left_shiftnot possible to detect an overloaded function operator<<, even if it is called on the next line. The program compiles and runs on GCC 4.5.1 or higher and clang 3.1.

To get around the obvious answers, I tried replacing the template function operator<<with specific versions for the various types used to no avail. I also tried various combinations of const-ness and l-value / r-value specifications for two types (different settings lead me to a compiler message indicating overload operator<<with ostream r-value). I also tried to realize my own trait, which at best gives me the same results as boost::has_left_shift.

Thanks in advance for any help that may be provided. I am also very grateful for a detailed explanation of why this behavior occurs and how the solution works. I am stretching the scope of my knowledge of templates and would like to know why this does not work, how I would think it.

#include <string>
#include <vector>
#include <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/type_traits/has_left_shift.hpp>

using namespace std;

struct Point {
    int x;
    int y;
    Point(int x, int y) : x(x), y(y) {}
    string getStr() const { return "("+boost::lexical_cast<string>(x)+","+boost::lexical_cast<string>(y)+")"; }
};

ostream& operator<<(ostream& stream, const Point& p)
{
    stream << p.getStr();
    return stream;
}

template <typename T>
ostream& operator<<(ostream& stream, const std::vector<T>& v)
{
    stream << "[";
    for(auto it = v.begin(); it != v.end(); ++it)
    {
        if(it != v.begin())
            stream << ", ";
        stream << *it;
    }
    stream << "]";
    return stream;
}

template <typename T>
void print(const string& name, T& t)
{
    cout << name << " has left shift = " << boost::has_left_shift<ostream , T>::value << endl;
    cout << "t = " << t << endl << endl;
}

int main()
{
    cout << boolalpha;

    int i = 1;
    print("int", i);

    string s = "asdf";
    print("std::string", s);

    Point p(2,3);
    print("Point", p);

    vector<int> vi({1, 2, 3});
    print("std::vector<int>", vi);

    vector<string> vs({"x", "y", "z"});
    print("std::vector<std::string>", vs);

    vector<Point> vp({Point(1,2), Point(3,4), Point(5,6)});
    print("std::vector<Point>", vp);
}
+5
source share
3 answers

, , , ++ ( ) . , , , ( UDT): ( ), ( , ).

, . , , , : , .

. Herb Sutter.

, , boost. std (ostream, string, vector), POD (int). std operator <<, () , . , () , boost, , operator <<, .

boost::has_left_shift, , - SFINAE , , , false value.

UPDATE:

, . , . ADL , std operator <<, - , operator << std .

, std ( , , operator <<), ++. , std ( ); , , vector<int>. , , .

: , . - Boost.TypeTraits. , , .

, boost::detail::has_left_shift_impl, , Boost.

, , :

namespace boost 
{ 
    namespace detail 
    { 
        namespace has_left_shift_impl
        {
            ostream& operator<<(ostream& stream, const Point& p)
            {
                stream << p.getStr();
                return stream;
            }

            template <typename T>
            std::ostream& operator<<(std::ostream& stream, const std::vector<T>& v)
            {
                stream << "[";
                for(auto it = v.begin(); it != v.end(); ++it)
                {
                    if(it != v.begin())
                        stream << ", ";
                    stream << *it;
                }
                stream << "]";
                return stream;
            }
        } 
    } 
}

.

: GCC 4.7.2 . , Clang 3.2, -, , boost::details::has_left_shift_impl has_left_shift.hpp. , .

+6

.

boost::has_left_shift<ostream , T>::value

. , operator<<. , .

, print , .

+3

boost::has_left_shift<>(), , . , std, boost::has_left_shift<>() .

std:

namespace std {

ostream& operator<<(ostream& stream, const Point& p); // definition omitted.

template <typename T>
ostream& operator<<(ostream& stream, const std::vector<T>& v); // definition omitted.

}

Since the standard prohibits defining new overloads in the namespace std, there is a way to pack custom printers for standard or third-party classes into your own namespace:

#include <vector>
#include <iostream>

namespace not_std {

// Can't directly overload operator<< for standard containers as that may cause ODR violation if
// other translation units overload these as well. Overload operator<< for a wrapper instead.

template<class Sequence>
struct SequenceWrapper
{
    Sequence* c;
    char beg, end;
};

template<class Sequence>
inline SequenceWrapper<Sequence const> as_array(Sequence const& c) {
    return {&c, '[', ']'};
}

template<class Sequence>
std::ostream& operator<<(std::ostream& s, SequenceWrapper<Sequence const> p) {
    s << p.beg;
    bool first = true;
    for(auto const& value : *p.c) {
        if(first)
            first = false;
        else
            s << ',';
        s << value;
    }
    return s << p.end;
}

} // not_std

int main() {
    std::vector<int> v{1,2,3,4};
    std::cout << not_std::as_array(v) << '\n';
}
+1
source

All Articles