std::cout
is an instance of std::ostream
. I can see the declaration of std::cout
in a file named /usr/include/c++/7/iostream
:
extern ostream cout; /// Linked to standard output
And std::ostream
is defined by typedef std::basic_ostream<char> std::ostream
.
What’s more, it seems that you can’t create an instance of std::ostream
. See this demo code snippet:
#include<iostream>
int main()
{
std::ostream os;
return 0;
}
Here is what the compiler complains about the code snippet above:
In file included from /opt/compiler-explorer/gcc-4.9.0/include/c++/4.9.0/iostream:39:0,
from <source>:1:
/opt/compiler-explorer/gcc-4.9.0/include/c++/4.9.0/ostream: In function 'int main()':
/opt/compiler-explorer/gcc-4.9.0/include/c++/4.9.0/ostream:384:7: error: 'std::basic_ostream<_CharT, _Traits>::basic_ostream() [with _CharT = char; _Traits = std::char_traits<char>]' is protected
basic_ostream()
^
<source>:5:18: error: within this context
std::ostream os;
^
The question arises, since the std::basic_ostream<_CharT, _Traits>::basic_ostream()
is marked protected, how std::cout
is created?
This link on CppReference seems not very meaningful. It does not clearly tell me how std::cout
is implemented and how std::cout
is created by the constructor of std::ostream
. As far as I can see, the most related information is:
The global objects
std::cout
andstd::wcout
control output to a stream buffer of implementation-defined type (derived fromstd::streambuf
), associated with the standard C output streamstdout
.
And nothing more.
I am working on Ubuntu
with gcc 4.9
Thanks to @NathanPierson.
He told me that
std::basic_ostream
has a constructor that takes a pointer to astd::basic_streambuf
object.std::cout
is initialized using a pointer to an instance of some implementation-defined derived class ofstd::basic_streambuf
.
, which moves me closer to the answer.
3
Answers
A compiler and its standard library implementation may cooperate using non-standard features which are not usable by mere programmers.
This is not necessary in this case because there is a quite standard public constructor:
If you have a
streambuf
ready, you can create an object of typeostream
, and so can the standard library.What is that
streambuf
exactly is a hidden implementation detail, but on a typical implementation it is probably an object of a custom class constructed fromstdout
(the C-style<cstdio>
file pointer).First things first, from https://en.cppreference.com/w/cpp/io/ios_base/Init :
Meh, let’s do a real code example. I will be using GCC C++ library. From https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/iostream#L73 , this is the important part:
Now we jump to the constructor of
ios_base::Init
class, in https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B98/ios_init.cc#L85 :The
_S_refcount
is there for when you would callios_base::Init::Init();
manually from a constructor of a static class, it protects against double initialization.The
stdio_sync_filebuf
is an internal buffer foristream
/ostream
and it is meant to handlecstdio
FILE*
operations to get/put input/output data, with implementation here https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/ext/stdio_sync_filebuf.h#L56 . It inherits fromstd::basic_streambuf
.So
cout
is constructed in-place withstdio_sync_filebuf<char>
as parameter. It is the first constructor mentioned here https://en.cppreference.com/w/cpp/io/basic_ostream/basic_ostream .Now, because the stuff is constructed in-place, you might wonder how is the memory allocated? From https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B98/globals_io.cc#L50 :
The objects are just empty
char
buffers of proper size and proper alignment.And yes, you can construct ostream yourself, with
__gnu_cxx::stdio_sync_filebuf
on GCC:Or, to be portable, you would write your own class that inherits from
std::streambuf
and constructostream
from it yourself. There are many examples online, like for example here https://stackoverflow.com/a/51250032/9072753 .I think a part of what the current answers are missing, and what is part of your question:
the name
std::cout
is also ‘magical’.This means that the standard library knows about it, and provides the OS-specific necessary connections to the terminal; using the respective (and OS-specific) system calls for output, etc.