Как работает " std:: cout

большинство IO stream манипуляторы обычные функции со следующей сигнатурой:

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; succeed, учитывая, что следующий пример терпит неудачу:

$ 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. Но каковы правила, регулирующие эту процедуру? Это похоже на реальную проблему для разрешения перегрузки, которая должна ответить на два вопроса сразу:

  1. какая специализация std::endl<CharT, Traits>() тут std::endl ссылаться?
  2. какая функция выполняет operator<< ссылаться?

вывод аргумента шаблона (1) должен произойти до разрешения перегрузки (2), но кажется, что (по крайней мере некоторая часть) (2) должна быть выполнена для того, чтобы (1) удалось.


несколько связанные, но не повторяющиеся вопросы:

ни один из этих вопросов и ни ответы на них не касаются работы шаблона вычет аргумент о том, что должно предшествовать разрешение перегрузки, но, должно быть, помогли последним.


следующий вопрос: как работает разрешение перегрузки, когда аргумент является перегруженной функцией?

4 ответов


на operator<< на вопрос of 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;

, потому что basic_ostream имеет шаблонную перегрузку operator<< что ожидает именно такой указатель на функцию:

basic_ostream<charT, traits>& operator<<(basic_ios<charT, traits>& (*pf)(basic_ios<charT, traits>&));

дано выражение утверждения формы

 std::cout << std::endl;

компилятор имеет информацию о типе std::cout - который является специализацией templated 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 are). Если он не может специализировать templated std::endl, он не может вывести тип возврата этой функции, поэтому вывод типа не выполняется.


вам нужно поставить

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, чтобы пропустить