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

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

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


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

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


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

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

Другие вопросы по тегам