Surprisingly the below code compiles and runs without error on a variety of compilers and versions.
#include <iostream>
int main() {
endl(std::cout);
return 0;
}
Ideone link
How does it compile? I am sure there is no endl
in global scope because a code like
std::cout << endl;
would fail unless using
is used or else you need std::endl
.
In the above program, the second std::endl isn’t technically necessary, since the program ends immediately afterward. However, it serves a few useful purposes. First, it helps indicate that the line of output is a “complete thought” (as opposed to partial output that is completed somewhere later in the code).
Writing ‘ ’ characters directly to the stream is more efficient since it doesn’t force a flush like std::endl. The following C++ program demonstrates the performance impact of std::endl. We write 100000 strings to two files once using std::endl and then again using ‘ ’.
The following C++ program demonstrates the performance impact of std::endl. We write 100000 strings to two files once using std::endl and then again using ‘ ’. In each case, we measure the execution time taken and print these times As seen from the output std::endl took nearly double the time.
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.
This behavior is called argument dependent lookup or Koenig lookup. This algorithm tells the compiler to not just look at local scope, but also the namespaces that contain the argument's type while looking for unqualified function call.
For ex:
namespace foo {
struct bar{
int a;
};
void baz(struct bar) {
...
}
};
int main() {
foo::bar b = {42};
baz(b); // Also look in foo namespace (foo::baz)
// because type of argument(b) is in namespace foo
}
About the piece of code referred in question text:
endl
or std::endl
is declared in std
namespace as following:
template< class CharT, class Traits >
std::basic_ostream<charT,traits>& endl( std::basic_ostream<CharT, Traits>& os );
or
std::ostream& endl (std::ostream& os);
And cout
or std::cout
is declared as
extern std::ostream cout;
So calling std::endl(std::cout);
is perfectly fine.
Now when one calls just endl(std::cout);
, because the type of argument cout
is from std namespace
, unqualified supposedly a function endl
is searched in std
namespace and it is found succesfully and confirmed to be a function and thus a call to qualified function std::endl
is made.
Further reading:
GOTW 30: Name Lookup
Why does 'std::endl' require the namespace qualification when used in the statement 'std::cout << std::endl;", given argument-dependent lookup?
It is the way the stream manipulators work. Manipulators are functions that passed to operator << as arguments. Then within the operator they are simply called.
So you have function declared like
template <class charT, class traits>
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);
and you pass its pointer to operator <<. And inside the operator that declared something like
ostream& ostream::operator << ( ostream& (*op)(ostream&));
the function is called.like
return (*endl )(*this);
Thus when you see record
std::cout << std::endl;
then std::endl
is function pointer that is passed to the operator <<
as argument.
In the record
std::endl( std::cout );
namespace prefix before name endl
may be omitted because in this case the compiler will use the Argument Dependent Lookup. Thus this record
endl( std::cout );
will compile successfully.
However if to enclose the function name in parentheses then ADL is not used and the following record
( endl )( std::cout );
will not compiled.
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