Most IO stream manipulators are regular functions with the following signature:
std::ios_base& func( std::ios_base& str );
However some manipulators (including the most frequently used ones - std::endl
and std::flush
) are templates of the following form:
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& func(std::basic_ostream<CharT, Traits>& os);
Then, how does the compilation of std::cout << std::endl;
succeed given that the following example fails:
$ cat main.cpp
#include <iostream>
int main()
{
auto myendl = std::endl;
std::cout << myendl;
}
$ g++ -std=c++11 main.cpp -o main
main.cpp: In function ‘int main()’:
main.cpp:5:24: error: unable to deduce ‘auto’ from ‘std::endl’
auto myendl = std::endl;
^
It is clear that the context (in std::cout << std::endl;
) helps the compiler to disambiguate the reference to std::endl
. But what are the rules that govern that procedure? It looks like a real challenge for overloading resolution, which has to answer two questions at once:
std::endl<CharT, Traits>()
does std::endl
refer to?operator<<
refer to?Template argument deduction (1) should happen before overload resolution (2), but it seems that (at least some part of) (2) is required to be performed in order for (1) to succeed.
Somewhat related but no-way duplicate questions are:
None of those questions and neither answers to them address the workings of template argument deduction that should precede overload resolution but must be helped by the latter.
Follow-up question: How does overload resolution work when an argument is an overloaded function?
cout << endl inserts a new line and flushes the stream(output buffer), whereas cout << “\n” just inserts a new line.
std::cout is used to output a value (cout = character output) std::cin is used to get an input value (cin = character input)
std::endl is known as an "io manipulator". This technique is intended to allow functions that manipulate the io stream's state to be set up "inline" with output commands by chaining << calls together.
According to C++ Reference, std::endl is actually a function.
The operator<<
in question is a member of std::basic_ostream
:
namespace std {
template <class charT, class traits = char_traits<charT> >
class basic_ostream {
public:
basic_ostream<charT,traits>& operator<<(
basic_ostream<charT,traits>& (*pf)(basic_ostream<charT,traits>&));
// ...
};
}
Since the call is to std::cout << std::endl
, or equivalently std::cout.operator<<(std::endl)
, we already know the exact instantiation of basic_ostream
: std::basic_ostream<char, std::char_traits<char>>
, aka std::ostream
. So the member function of cout
looks like
std::ostream& operator<<(std::basic_ostream<char, std::char_traits<char>>& (*pf)
(std::basic_ostream<char, std::char_traits<char>>&));
This member function is not a function template, just an ordinary member function. So the question remaining, is can it be called with the name std::endl
as an argument? Yes, initializing the function argument is equivalent to a variable initialization, as though we had written
std::basic_ostream<char, std::char_traits<char>>& (*pf)
(std::basic_ostream<char, std::char_traits<char>>&) = std::endl;
Given a statement expression of the form
std::cout << std::endl;
The compiler has information about the type of std::cout
- which is a specialisation of the templated std::basic_ostream
which looks something like (omitting the containing namespace std
).
template <class charT, class traits = char_traits<charT> >
class basic_ostream
{
public:
basic_ostream<charT,traits>& operator<<(
basic_ostream<charT,traits>& (*pf)(basic_ostream<charT,traits>&));
// other members ....
};
Since the compiler has information about the type of std::cout
it knows what charT
and traits
are to specialise the preceeding template.
The above causes std::endl
in the expression std::cout << std::endl
to match to the specific std::basic_ostream<charT, traits>& endl( std::basic_ostream<charT, traits>&)
.
The reason type deduction doesn't work in
auto myendl = std::endl;
is because std::endl
is a templated function, and this declaration provides no information to specialise that template (i.e. picking what charT
or traits
are). If it can't specialise the templated std::endl
, it can't infer that function's return type, so the type deduction fails.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With