Подтвердить что ты не робот

Как "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).


Несколько взаимосвязанных, но безрезультатных вопросов:

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


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

4b9b3361

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