Как "std:: cout << std:: endl;" компилировать?
Большинство манипуляторы потоков IO являются регулярными функциями со следующей сигнатурой:
std::ios_base& func( std::ios_base& str );
Однако некоторые манипуляторы (включая наиболее часто используемые - std::endl
и std::flush
) являются шаблонами следующей формы:
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& func(std::basic_ostream<CharT, Traits>& os);
Затем, как удается выполнить компиляцию std::cout << std::endl;
, учитывая, что приведен следующий пример:
$ cat main.cpp
#include <iostream>
int main()
{
auto myendl = std::endl;
std::cout << myendl;
}
$ g++ -std=c++11 main.cpp -o main
main.cpp: In function ‘int main()’:
main.cpp:5:24: error: unable to deduce ‘auto’ from ‘std::endl’
auto myendl = std::endl;
^
Понятно, что контекст (в std::cout << std::endl;
) помогает компилятору рассортировать ссылку на std::endl
. Но каковы правила, регулирующие эту процедуру? Это выглядит как настоящая проблема перегрузки разрешения, которая должна отвечать сразу на два вопроса:
- К какой специализации
std::endl<CharT, Traits>()
относится std::endl
к?
- Какую функцию выполняет
operator<<
?
Вывод аргумента шаблона (1) должен произойти до разрешения перегрузки (2), но кажется, что (по крайней мере, часть) (2) требуется выполнить для успешного выполнения (1).
Несколько взаимосвязанных, но безрезультатных вопросов:
Ни один из этих вопросов и ни один ответ на них не затрагивают работу вывода аргумента шаблона, который должен предшествовать разрешению перегрузки, но должен помогать последнему.
Последующий вопрос: Как работает разрешение перегрузки, когда аргумент является перегруженной функцией?
Ответы
Ответ 1
Рассматриваемый operator<<
является членом std::basic_ostream
:
namespace std {
template <class charT, class traits = char_traits<charT> >
class basic_ostream {
public:
basic_ostream<charT,traits>& operator<<(
basic_ostream<charT,traits>& (*pf)(basic_ostream<charT,traits>&));
// ...
};
}
Поскольку вызов равен std::cout << std::endl
или эквивалентно std::cout.operator<<(std::endl)
, мы уже знаем точное описание basic_ostream
: std::basic_ostream<char, std::char_traits<char>>
, aka std::ostream
. Таким образом, функция-член cout
выглядит как
std::ostream& operator<<(std::basic_ostream<char, std::char_traits<char>>& (*pf)
(std::basic_ostream<char, std::char_traits<char>>&));
Эта функция-член не является шаблоном функции, а просто обычной функцией-членом. Таким образом, оставшийся вопрос, может ли он быть вызван с именем std::endl
в качестве аргумента? Да, инициализация аргумента функции эквивалентна инициализации переменной, как если бы мы писали
std::basic_ostream<char, std::char_traits<char>>& (*pf)
(std::basic_ostream<char, std::char_traits<char>>&) = std::endl;
Ответ 2
Поскольку basic_ostream
имеет шаблонную перегрузку operator<<
, которая ожидает такой указатель функции:
basic_ostream<charT, traits>& operator<<(basic_ios<charT, traits>& (*pf)(basic_ios<charT, traits>&));
Ответ 3
Учитывая выражение выражения формы
std::cout << std::endl;
Компилятор имеет информацию о типе std::cout
- который является специализацией шаблона std::basic_ostream
, который выглядит примерно так (исключая содержащее namespace std
).
template <class charT, class traits = char_traits<charT> >
class basic_ostream
{
public:
basic_ostream<charT,traits>& operator<<(
basic_ostream<charT,traits>& (*pf)(basic_ostream<charT,traits>&));
};
Поскольку у компилятора есть информация о типе std::cout
, он знает, что charT
и traits
должны специализировать предыдущий шаблон.
Приведенные выше причины std::endl
в выражении std::cout << std::endl
соответствуют конкретному std::basic_ostream<charT, traits>& endl( std::basic_ostream<charT, traits>&)
.
Вывод типа причины не работает в
auto myendl = std::endl;
заключается в том, что std::endl
является шаблонизированной функцией, и это объявление не предоставляет никакой информации, чтобы специализировать этот шаблон (т.е. выбирать, что charT
или traits
). Если он не может специализировать шаблонный std::endl
, он не может вывести этот возвращаемый тип функции, поэтому сбой вывода типа.
Ответ 4
Вам нужно поставить < < < до конца. Он является членом ofstream:
namespace std {
template <class charT, class traits = char_traits<charT> >
class basic_ostream {
public:
basic_ostream<charT,traits>& operator<<(
basic_ostream<charT,traits>& (*pf)(basic_ostream<charT,traits>&));
// ...
};
}
endl работает как "/n", чтобы пропустить строку, поэтому вам нужна строка cout, чтобы пропустить