Почему мне приходится писать 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
Без ADL (поиск Koenig)
std::cout std:: << "Hello World" ;//this won't compile
Чтобы скомпилировать его, нам нужно использовать его более уродливую форму
std::operator<<(std::cout, "Hello, world!");
Итак, чтобы избежать такого уродливого синтаксиса, мы должны оценить Koenig Lookup:)
Ответ 3
Компилятор видит, что аргументы в < < < являются объектом std:: ostream и строкой, и поэтому он может найти правильный оператор < < определение на основе этого.
Вы можете рассматривать мыслительные типы операторов (или, действительно, любую функцию) как часть своего имени.