Why I have to write std::cout and not also std::<<

C++

C++ Problem Overview


Why do I have to write std::cout and not also std::<< in a line of code like this:

#include <iostream>

int main() {
    std::cout << "Hello, world!";
    return 0;
}

cout comes from std library, and isn't << usually used to do bits shifting? So, why don't I have to write the scope operator :: also before <<, since it is used also with another meaning? How the compiler knows that after std::cout, << means another thing?

C++ Solutions


Solution 1 - C++

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 - C++

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 - C++

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFrancesco BonizziView Question on Stackoverflow
Solution 1 - C++dypView Answer on Stackoverflow
Solution 2 - C++P0WView Answer on Stackoverflow
Solution 3 - C++alecbzView Answer on Stackoverflow