Blog

How Does std::endl Work?

Published:

At first we might think std::endl is just a constant for '\n', after all, it does add a new line to the output. However, it also flushes the stream. Simply outputting '\n' may also flush the stream in some implementations, however that is not part of the standard. Taking a closer look, this is how std::endl is declared:

template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& endl( std::basic_ostream<CharT, Traits>& os );

That looks a lot like a function. That's because it is a function; so are the other stream manipulators. So how does std::endl work? We never use it like a function, no one would ever write code like this:

std::cout << "Hello World!";
std::endl(std::cout);

We could, it does work, but it's arguably not as readable as the standard way:

std::cout << "Hello World!" << std::endl;

This passes a pointer to the std::endl function to operator<<. If we take a closer look at operator<<, we see that it is a template:

template< class Ostream, class T >
Ostream&& operator<<( Ostream&& os, const T& value );

So, there must be a template specialization that takes a function pointer with the same signature as std::endl. The specialization must call that function and pass it a reference to the ostream. To prove this let's try making our own stream manipulator. There's no standard stream manipulator for ANSI escape codes, so let's use that.

#include <iostream>

namespace ansi {

template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& fgred( std::basic_ostream<CharT, Traits>& os ) {
	os << "\033[31m";
	return os;
}

template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& reset( std::basic_ostream<CharT, Traits>& os ) {
	os << "\033[0m";
	return os;
}

} // namespace ansi

int main() {
	std::cout << "This text is " << ansi::fgred << "red" << ansi::reset << "!" << std::endl;
	return 0;
}
Here we have two functions ansi::fgred and ansi::reset. When those functions excute they should output the corresponding ANSI escape codes on the stream. When we run it:
# g++ ansi_test.cpp && ./a.out
This text is red!
Look at that, it works! This is particularly clever, we can add new stream manipulators without needing to modify operator<< or any other part of ostream. Not only does this allow new stream manipulators to be easily added to the standard library, it also allows developers to add there own stream manipulators, as we've seen.