Почему мне приходится писать std:: cout, а не std:: <<

Почему мне приходится писать std::cout, а не также std::<< в строке кода следующим образом:

#include <iostream>

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

cout поступает из библиотеки std и не << обычно используется для смещения битов? Итак, почему бы мне не написать оператор области :: еще до <<, так как он используется также с другим значением? Как компилятор знает, что после std::cout, << означает другую вещь?

Ответ 1

Во-первых, компилятор будет смотреть на типы слева и справа от <<. std::cout имеет тип std::ostream, строковый литерал имеет массив типов из 15 const char. Поскольку левый тип класса, он будет искать функцию с именем operator<<. Вопрос в том, где он будет выглядеть?

Поиск этого имени operator<< - это так называемый неквалифицированный поиск, потому что имя функции не имеет значения, например std::operator<<. Неквалифицированный поиск имен функций вызывает зависящий от аргумента поиск. Поиск зависимых от аргументов поиска будет искать в классах и пространствах имен, связанных с типами аргументов.

Когда вы включаете <iostream>, свободная функция подписи

template<typename traits>
std::basic_ostream<char, traits>& operator<<(std::basic_ostream<char, traits>&,
                                             const char*);

объявлен в пространстве имен std. Это пространство имен связано с типом std::cout, поэтому эта функция будет найдена.

std::ostream - это просто typedef для std::basic_ostream<char, std::char_traits<char>>, а массив из 15 const char может быть неявно преобразован в char const* (указывая на первый элемент массива). Поэтому эту функцию можно вызывать с двумя типами аргументов.

Существуют и другие перегрузки operator<<, но указанная выше функция является наилучшим соответствием для типов аргументов и выбранного в этом случае.


Простой пример зависящего от аргумента поиска:

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. Поскольку эта функция является оператором, фактический поиск немного сложнее. Он просматривается с помощью квалифицированного поиска в области первого аргумента (если это тип класса), т.е. Как функция-член. Кроме того, выполняется неквалифицированный поиск, но игнорируется все функции-члены. Результат несколько отличается, потому что неквалифицированный поиск на самом деле похож на двухэтапную процедуру, где зависимым от аргумента поиска является второй шаг. Если первый шаг находит функцию-член, второй этап не выполняется, то есть поиск по запросу не используется.

Для сравнения:

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
    }
}

в

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
    }
}

Ответ 2

В std::cout << "Hello, world!"; //calls std:::operator <<

Это достигается с помощью зависимого от Аргумента поиска имени (ADL, aka Koenig Lookup)

Хотя у нас есть только один отборочный std, но есть две вещи, которые возникают из пространства имен std

  • cout
  • <<

Без ADL (поиск Koenig)

std::cout std:: << "Hello World" ;//this won't compile

Чтобы скомпилировать его, нам нужно использовать его более уродливую форму

std::operator<<(std::cout, "Hello, world!");

Итак, чтобы избежать такого уродливого синтаксиса, мы должны оценить Koenig Lookup:)

Ответ 3

Компилятор видит, что аргументы в < < < являются объектом std:: ostream и строкой, и поэтому он может найти правильный оператор < < определение на основе этого.

Вы можете рассматривать мыслительные типы операторов (или, действительно, любую функцию) как часть своего имени.