1

I have a set of pairs of integers and I want to print it, so I overloaded << operator for set and pair classes as :

template<typename T, typename U>
inline ostream& operator<<(ostream& os, pair<T,U> &p){
    os<<"("<<p.first<<","<<p.second<<")";
    return os;
}


template<typename T>
inline ostream& operator<<(ostream& os, set<T> &s){
    os<<"{";
    for(auto it = s.begin() ; it != s.end() ; it++){
        if(it != s.begin())
            os<<",";
        os<<*it;
    }
    os<<"}";
    return os;
}

When I create a set and output it like

set<pair<int,int>> s;
cout<<s<<endl;

It gives the errors :

cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’
   os<<*it;

and

initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = std::pair<int, int>]’
     operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)

I don't know what is the problem, the errors are very cryptic. Moreover, if I create a set of integers and print it, it works fine.

Happy Mittal
  • 3,667
  • 12
  • 44
  • 60

2 Answers2

5

The type of it in auto it = s.begin() is a const_iterator(source). as such when you call os<<*it; you need a function that can take a const pair. If you change you code to this it will work:

#include <iostream>
#include <set>
#include <utility>

using namespace std;

template<typename T, typename U>
inline ostream& operator<<(ostream& os, const pair<T,U> &p){
    os<<"("<<p.first<<","<<p.second<<")";
    return os;
}


template<typename T>
inline ostream& operator<<(ostream& os, const set<T> &s){
    os<<"{";
    for(auto it = s.begin() ; it != s.end() ; it++){
        if(it != s.begin())
            os<<",";
        os<<*it;
    }
    os<<"}";
    return os;
}

int main()
{
    set<pair<int,int>> s {{1,2}};
    cout<<s<<endl;
}

Live Example

I would also suggest that you always take the second parameter as a const & for

  1. You can bind a temporary to a const & (ex function returns)

  2. You should not be modifying the container during output so this uses the C++ type system to enforce that.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
2

The stream operators are not meant to modify the value their are passed. Hence, you should accept a const reference instead of a mutable reference:

template<typename T, typename U>
inline ostream& operator<<(ostream& os, const pair<T,U> &p){
    os<<"("<<p.first<<","<<p.second<<")";
    return os;
}


template<typename T>
inline ostream& operator<<(ostream& os, const set<T> &s){
    os<<"{";
    for(auto it = s.begin() ; it != s.end() ; it++){
        if(it != s.begin())
            os<<",";
        os<<*it;
    }
    os<<"}";
    return os;
}
slaphappy
  • 6,894
  • 3
  • 34
  • 59