GingerPlusPlus
GingerPlusPlus

Reputation: 5616

Can't use overloaded operator<< to print object's value

I wrote tuple implementation, which seems to work:

template<typename T, typename... U>
struct tuple{
    T first;
    tuple<U...> second;
    tuple()=default;
    tuple(T t, U... u):first(t), second(u...){}
    std::ostream& print(std::ostream& stream){
        stream<<first<<", ";
        return second.print(stream); //not using << to avoid extra () in output
    }
};

template<typename T>
struct tuple<T>{
    T first;
    tuple()=default;
    tuple(T t):first(t){}
    operator T&(){
        return first;
    }
    std::ostream& print(std::ostream& stream){
        return stream<<first;
    }
};

template<typename... T>
inline auto mk_tuple(T... t){
    return tuple<T...>(t...);
}

I have operator<< overloaded this way:

template<typename... T>
std::ostream& operator<<(std::ostream &stream, tuple<T...> &out){
    stream<<'(';
    return out.print(stream)<<')';
}

When I try to use it this way: std::cout<<mk_tuple(1, 2, 3, "xyz", 'c'); I get

error: cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’

However, mk_tuple(1, 2, 3, "xyz", 'c').print(std::cout) works.
(but it's unsatisfactory, because it's not obvious syntax in C++).

How should I overload operator<< to be able to use it correctly in this case?

Upvotes: 2

Views: 474

Answers (1)

Daniel Frey
Daniel Frey

Reputation: 56863

The signature is wrong, you need:

template<typename... T>
std::ostream& operator<<(std::ostream &stream, const tuple<T...> &out){
//                                             ^^^^^

since you are not going to modify the tuple on output. This then allows your operator to be called with constant or temporary values.

It also requires you to mark the print method as const:

std::ostream& print(std::ostream& stream) const {
//                                        ^^^^^

Generally, google "const correctness" and learn about this essential paradigm in C++.


EDIT: Got it! Try this signature:

template<typename T, typename... U>
std::ostream& operator<<(std::ostream &stream, const tuple<T, U...> &out){
    stream<<'(';
    return out.print(stream)<<")";
}

as some older versions of GCC seem to have a bug that prevents them from properly deducing just T...s.

Upvotes: 3

Related Questions