Functions with "reference" return types?

example)

1
2
3
4
ostream& operator<<(...)
{
//...
}


I don't understand what the reference operator is doing after the return type. Can anyone explain please?
Last edited on
Streams cannot be passed by value, so a reference to the same stream passed as argument is returned. This is simply a convenience.

For example, suppose you have a 2D cartesian point class with the usual constructors:

1
2
3
4
5
6
7
template <typename T>
struct point
{
  T x, y;
  point(): x(), y() { }
  point( T x, T y ): x(x), y(y) { }
};
1
2
3
int main()
{
  point<int> p (10,-2);

At some junction, you will probably want to be able to print that point. What you'd like to see is:

    (10,-2)  

So you write a function that knows how to print a point:

1
2
3
4
5
template <typename T>
void print( const point <T> & p )
{
  std::cout << "(" << p.x << "," << p.y << ")";
}
 
  print( p );
(10,-2)

A little later you realize it would be very convenient to be able to print that point to any output stream. You modify your function:

1
2
3
4
5
template <typename T>
void print( std::ostream& outs, const point <T> & p )
{
  outs << "(" << p.x << "," << p.y << ")";
}
1
2
3
  std::cerr << "Sorry, the point ";
  print( std::cerr, p );
  std::cerr << " is invalid.\n";

What would be really nice, of course, is to be able to chain all those statements together into one line. For that, we'd need to have the function return a stream to work with:

1
2
3
4
5
template <typename T>
std::ostream& print( std::ostream& outs, const point <T> & p )
{
  return outs << "(" << p.x << "," << p.y << ")";
}
 
  std::cerr << "Sorry, the point "; print( std::cerr, p ) << " is invalid.\n";

Well -- technically, that's still two statements. We can make it one:
 
  print( std::cerr << "Sorry, the point ", p ) << " is invalid.\n";

Notice that at this point we are exactly one step off from an insertion operator:

1
2
3
4
5
template <typename T>
std::ostream& operator << ( std::ostream& outs, const point <T> & p )
{
  return outs << "(" << p.x << "," << p.y << ")";
}
 
  std::cerr << "Sorry, the point " << p << " is invalid.\n";

That last line of code is automagically converted by the compiler into a function call exactly like our last print() example above.


The utility of the reference, then, is to allow you to chain a series of function calls using the same stream as argument.

Hope this helps.
What about cases where we are not dealing with streams?
e.g)

1
2
3
4
5
const Date& default_date()
{
static const Date dd(1970,1,1);
return dd;
}
Last edited on
That particular example looks like a design flaw.


References fill a specific need. And sometimes you just want to be able to return the reference, the same as any other kind of thing you may wish to return from a function.


Your Date class example is a common idiom for singletons -- which is a form of global variable needing some safe initialization.
I see, thank you very much!
Topic archived. No new replies allowed.