简体   繁体   English

“ std :: cout << std :: endl;”如何编译?

[英]How does “std::cout << std::endl;” compile?

Most IO stream manipulators are regular functions with the following signature: 大多数IO流操纵器是具有以下签名的常规功能:

std::ios_base& func( std::ios_base& str );

However some manipulators (including the most frequently used ones - std::endl and std::flush ) are templates of the following form: 但是,某些操纵器(包括最常用的操纵器std::endlstd::flush )是以下形式的模板:

template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& func(std::basic_ostream<CharT, Traits>& os);

Then, how does the compilation of std::cout << std::endl; 然后,如何编译std::cout << std::endl; succeed given that the following example fails: 鉴于以下示例失败,成功:

$ 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;
                        ^

It is clear that the context (in std::cout << std::endl; ) helps the compiler to disambiguate the reference to std::endl . 显然,上下文(在std::cout << std::endl; )有助于编译器消除对std::endl的引用的歧义。 But what are the rules that govern that procedure? 但是,控制该程序的规则是什么? It looks like a real challenge for overloading resolution, which has to answer two questions at once: 对于超载分辨率而言,这似乎是一个真正的挑战,它必须立即回答两个问题:

  1. Which specialization of std::endl<CharT, Traits>() does std::endl refer to? std::endl指的是std::endl<CharT, Traits>()哪个专业化?
  2. Which function does the operator<< refer to? operator<<指的是哪个功能?

Template argument deduction (1) should happen before overload resolution (2), but it seems that (at least some part of) (2) is required to be performed in order for (1) to succeed. 模板自变量推导(1)应该在重载解决方案(2)之前发生,但是似乎(2)的至少一部分必须执行才能使(1)成功。


Somewhat related but no-way duplicate questions are: 一些相关但绝非重复的问题是:

None of those questions and neither answers to them address the workings of template argument deduction that should precede overload resolution but must be helped by the latter. 这些问题都没有,也没有答案解决模板参数推导的工作,这种推导应该在重载解析之前,但必须得到后者的帮助。


Follow-up question: How does overload resolution work when an argument is an overloaded function? 后续问题: 当参数是重载函数时,重载解析如何工作?

The operator<< in question is a member of std::basic_ostream : 有问题的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>&));
        // ...
    };
}

Since the call is to std::cout << std::endl , or equivalently std::cout.operator<<(std::endl) , we already know the exact instantiation of basic_ostream : std::basic_ostream<char, std::char_traits<char>> , aka std::ostream . 由于调用是std::cout << std::endl或等效的std::cout.operator<<(std::endl) ,我们已经知道basic_ostream的确切实例: std::basic_ostream<char, std::char_traits<char>> ,又名std::ostream So the member function of cout looks like 所以cout的成员函数看起来像

std::ostream& operator<<(std::basic_ostream<char, std::char_traits<char>>& (*pf)
    (std::basic_ostream<char, std::char_traits<char>>&));

This member function is not a function template, just an ordinary member function. 该成员函数不是函数模板,而只是普通成员函数。 So the question remaining, is can it be called with the name std::endl as an argument? 所以剩下的问题是,是否可以使用名称std::endl作为参数来调用它? Yes, initializing the function argument is equivalent to a variable initialization, as though we had written 是的,初始化函数参数等效于变量初始化,就像我们已经写过一样

std::basic_ostream<char, std::char_traits<char>>& (*pf)
    (std::basic_ostream<char, std::char_traits<char>>&) = std::endl;

因为basic_ostream具有operator<<的模板重载,所以basic_ostream需要这样的函数指针:

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

Given a statement expression of the form 给定一个形式的语句表达

 std::cout << std::endl;

The compiler has information about the type of std::cout - which is a specialisation of the templated std::basic_ostream which looks something like (omitting the containing namespace std ). 编译器具有有关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>&));
};

Since the compiler has information about the type of std::cout it knows what charT and traits are to specialise the preceeding template. 由于编译器具有有关std::cout的类型的信息,因此它知道专用于charT模板的charTtraits是什么。

The above causes std::endl in the expression std::cout << std::endl to match to the specific std::basic_ostream<charT, traits>& endl( std::basic_ostream<charT, traits>&) . 上述原因std::endl中表达std::cout << std::endl以匹配特定std::basic_ostream<charT, traits>& endl( std::basic_ostream<charT, traits>&)

The reason type deduction doesn't work in 原因类型推论不适用于

 auto myendl = std::endl;

is because std::endl is a templated function, and this declaration provides no information to specialise that template (ie picking what charT or traits are). 这是因为std::endl是模板函数,并且此声明未提供专门用于该模板的信息(即,选择什么charTtraits )。 If it can't specialise the templated std::endl , it can't infer that function's return type, so the type deduction fails. 如果不能专用于模板化的std::endl ,则无法推断该函数的返回类型,因此类型推导失败。

You need to put << before the endl. 您需要将<<放在endl之前。 It is a member of ofstream: 它是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 works like a "/n" to skip the line, so you need a cout line to skip endl就像“ / n”一样跳过该行,因此您需要一个cout行来跳过

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM