Why I have to write std::cout and not also std::<<
Solution 1:
First, the compiler will look at the types to the left and right of <<
. std::cout
is of type std::ostream
, the string literal is of type array of 15 const char
. As the left is of class type, it will search for a function named operator<<
. The question is, where will it look?
The lookup for this name operator<<
is a so-called unqualified lookup, because the function name isn't qualified like std::operator<<
. Unqualified lookup for function names invokes argument-dependent lookup. The argument-dependent lookup will search in the classes and namespaces associated with the argument types.
When you include <iostream>
, a free function of the signature
template<typename traits>
std::basic_ostream<char, traits>& operator<<(std::basic_ostream<char, traits>&,
const char*);
has been declared in namespace std
. This namespace is associated with the type of std::cout
, therefore this function will be found.
std::ostream
is just a typedef for std::basic_ostream<char, std::char_traits<char>>
, and the array of 15 const char
can be converted implicitly to a char const*
(pointing to the first element of the array). Therefore, this function can be called with the two argument types.
There are other overloads of operator<<
, but the function I mentioned above is the best match for the argument types and the one selected in this case.
A simple example of argument-dependent lookup:
namespace my_namespace
{
struct X {};
void find_me(X) {}
}
int main()
{
my_namespace::X x;
find_me(x); // finds my_namespace::find_me because of the argument type
}
N.B. As this function is a operator, the actual lookup is a bit more complex. It is looked up via qualified lookup in the scope of the first argument (if that's of class type), i.e. as a member function. Additionally, unqualified lookup is performed, but ignoring all member functions. The result is slightly different, because unqualified lookup is actually like a two-step procedure, where argument-dependent lookup is the second step. If the first step finds a member function, the second step is not performed, i.e. argument-dependent lookup is not used.
Compare:
namespace my_namespace
{
struct X
{
void find_me(X, int) {}
void search();
};
void find_me(X, double) {}
void X::search() {
find_me(*this, 2.5); // only finds X::find_me(int)
// pure unqualified lookup (1st step) finds the member function
// argument-dependent lookup is not performed
}
}
to:
namespace my_namespace
{
struct X
{
void operator<<(int) {}
void search();
};
void operator<<(X, double) {}
void X::search() {
*this << 2.5; // find both because both steps are always performed
// and overload resolution selects the free function
}
}
Solution 2:
In std::cout << "Hello, world!"; //calls std:::operator <<
This is achieved with Argument-dependent name lookup (ADL, aka Koenig Lookup)
Although we have only one std
qualifier but there are two things that comes up from std
namespace
cout
<<
Without ADL, (Koenig Lookup)
std::cout std:: << "Hello World" ;//this won't compile
In order to compile it, we need to use it more uglier form
std::operator<<(std::cout, "Hello, world!");
So to avoid such ugly syntax we must appreciate Koenig Lookup :)
Solution 3:
The compiler sees that the arguments to << are an std::ostream object and a string, and so is able to locate the proper operator<< definition based on this.
You can sort of think of the argument types of an operator (or really, any function) as part of its name.