Как работает "std::cout << std::endl;" компилировать?
Большинство потоковых манипуляторов ввода-вывода являются обычными функциями со следующей сигнатурой:
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) был успешным.
Несколько связанные, но не повторяющиеся вопросы:
- Работает ли std:: endl с cout и wcout?
- Почему endl(std::cout) компилируется
- Как работает std:: flush?
Ни один из этих вопросов, ни ответы на них не касаются работы вывода аргументов шаблона, которая должна предшествовать разрешению перегрузки, но должна помогать последним.
Дополнительный вопрос: как работает разрешение перегрузки, когда аргумент является перегруженной функцией?
4 ответа
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>>
ака 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;
Дано выражение выражения вида
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
, он не может определить тип возвращаемого значения этой функции, поэтому вывод типа завершается ошибкой.
Так как basic_ostream
имеет шаблонную перегрузку operator<<
что ожидает именно такой указатель на функцию:
basic_ostream<charT, traits>& operator<<(basic_ios<charT, traits>& (*pf)(basic_ios<charT, traits>&));
Вам нужно поставить << до конца. Является членом 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, чтобы пропустить