2016-10-29 5 views
14

Большинство IO stream manipulators являются регулярными функциями со следующей подписью:Как «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), но кажется, что для выполнения (1) требуется (хотя бы часть) (2) выполнить.


Несколько связанных, но не-полосная дублирующие вопросы:

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


последующий вопрос:How does overload resolution work when an argument is an overloaded function?

+0

@ πάνταῥεῖ Этот вопрос не является дубликатом. Это даже не то, что ответ Дитмара Кюля содержит информацию, запрошенную в моем очень другом вопросе (он только утверждает, что * «Компилятор автоматически выводит аргументы шаблона» *, в то время как мне любопытно, как работает этот вывод). – Leon

+0

_ @ Leon_ Можете ли вы изложить свои аргументы в своем вопросе, почему вы думаете, что это не ясно объяснено в ответах Дитмара или других там, пожалуйста? Возможно, я подумаю, что ты снова закроешь свой вопрос. –

+0

@ πάνταῥεῖ Я уже добавил эти аргументы в свое редактирование (до того, как вопрос был закрыт). – Leon

ответ

10

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; 
+0

Спасибо. В вашем ответе были намеки, которые помогли мне лучше понять разрешение перегрузки в этом случае. Я опубликовал более абстрактный вопрос: http://stackoverflow.com/questions/40343663/how-does-overload-resolution-work-when-an-argument-is-an-overloaded-function – Leon

4

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

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

И как в этом случае работает перегрузка разрешений (см. Обновленный вопрос)? – Leon

-2

Вам нужно положить < < до End L,. Он является членом 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>&)); 
    // ... 
}; 

} 

епсИ работает как «/ п», чтобы пропустить строку, так что вам нужно COUT линию, чтобы пропустить

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, он не может вывести тип возвращаемой функции, поэтому сбой вывода типа невозможен.

Смежные вопросы