This post is part of a series of tutorials on C++

One operator that can be insanely useful for debugging is to code stream operators, also know as operator<<() and operator>>(). You’ll notice that << and >> are the same symbols used for cout and cin respectively. Having these operators implemented can be useful to writing contents of a class to standard input and standard output. They can also be useful to parsing the contents of a file directly into instances of a class, as file streams use the same operators.

The basic form is quite easy to implement:

class Foo {
  friend std::ostream& operator<<(std::ostream& os, const Foo& obj) {
    // write obj to stream
    return os;
  }
}

The above is a template for the stream insertion operator, whose purpose is to “insert” obj into the stream os. Note that this function returns the stream itself. This allows for chain, e.g.

int main() {
  Foo a,b;
  std:: cout << a << b;
}

As the expression std::cout << a << b will evaluate as (std::cout << a) << b, allowing one to first evaluate to std::cout.operator<<(a), which then returns the updated std::cout so that you can do std::cout.operator(b) after inserting a into the stream.

It’s handy to make this function a friend so that it can access private members, but this is not necessary. Some people prefer to keep it an external method, and instead code a function that converts the class member to a string. This is extremely similar to Java’s toString() method, and looks like this:

class Foo {
  operator std::string() const {
    //make some king of string, str, out of the object
    return str;
  }
}
std::ostream& operator<<(std::ostream& os, const Foo& obj) {
  os << (std::string) obj;
  return os;
}

There’s a lot going on in the above. First off, the easy part to understanding is how operator<<() works now. First, we take our instance of Foo, obj, and cast obj to be of type string. What is a bit more subtle is that the cast that we did uses an operator! In fact, in C++, you can code all sorts of casting operators to cast your complex types to a variety of other types, both complex (like std::string) or primitives (like int and float). Our casting operator could have just as easily been a member function std::string toString(), but making it an operator allows us to use C++-style casting. Neat!

Note that these cast operators seem a bit weird. They do not have a return type stated, although they are meant to return whatever type you are casting to.

Let’s see a version of this in action on our ThreeLetterWord class from the tutorials on Copy Constructors and Assignment Operators. Note that we were able to get rid of the mundane print() function:

The above leaves a lot open-ended. Folks will often using the std::string() cast to format complex types in ways that are easy to quickly glance at and debug. There are lots of options here.

Stream extraction operators can be really useful for parsing data. Their basic form looks like this:

class Foo {
  std::istream& operator>>(std::istream& is, Foo& obj)
  {
    // read obj from stream
  
    if( /* no valid Foo found in stream */ )
      is.setstate(std::ios::failbit);
  
    return is;
  }
}

The above logic is a bit more complicated, since you have to be careful to check that a valid instance of Foo is found. Typically, you might do this by reading the individual members of Foo one at a time. For example, in our ThreeLetterWord class, we might first read each character of the ThreeLetterWord, and as long as we found three characters, then updated obj. Otherwise, we set the failbit and return.



Back to C++ Tutorials