"std :: cout << std :: endl;"怎么样?编译?

Leo*_*eon 15 c++ templates overload-resolution iomanip

大多数IO流操纵器都是具有以下签名的常规函数​​:

std::ios_base& func( std::ios_base& str );
Run Code Online (Sandbox Code Playgroud)

但是,一些操纵器(包括最常用的操纵器 - std::endl和std::flush)是以下形式的模板:

template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& func(std::basic_ostream<CharT, Traits>& os);
Run Code Online (Sandbox Code Playgroud)

然后,如果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;
                        ^
Run Code Online (Sandbox Code Playgroud)

很明显,上下文(in std::cout << std::endl;)有助于编译器消除对引用的歧义std::endl.但是,管理该程序的规则是什么?对于重载分辨率来说,这似乎是一个真正的挑战,它必须同时回答两个问题:

  1. 哪个专业std::endl<CharT, Traits>()确实std::endl参考?
  2. operator<<参考哪个功能?

模板参数推导(1)应该在重载决策(2)之前发生,但似乎(至少某些部分)(2)需要执行以便(1)成功.


有些相关但无法重复的问题是:

这些问题都没有,也没有对它们的回答都解决了模板参数推断的工作原理,它应该在重载解决之前,但必须得到后者的帮助.


后续问题: 当参数是一个重载函数时,重载解析如何工作?

asc*_*ler 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>&));
        // ...
    };
}
Run Code Online (Sandbox Code Playgroud)

由于调用是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>>&));
Run Code Online (Sandbox Code Playgroud)

该成员函数不是函数模板,只是普通的成员函数.所以剩下的问题是,它可以用名称std::endl作为参数来调用吗?是的,初始化函数参数等同于变量初始化,就像我们编写的那样

std::basic_ostream<char, std::char_traits<char>>& (*pf)
    (std::basic_ostream<char, std::char_traits<char>>&) = std::endl;
Run Code Online (Sandbox Code Playgroud)


120*_*arm 5

因为basic_ostream有一个模板化的重载operator<<,只需要这样一个函数指针:

basic_ostream<charT, traits>& operator<<(basic_ios<charT, traits>& (*pf)(basic_ios<charT, traits>&));
Run Code Online (Sandbox Code Playgroud)


Pet*_*ter 5

给定形式的语句表达式

 std::cout << std::endl;
Run Code Online (Sandbox Code Playgroud)

编译器有关于类型的信息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>&));

        // other members ....
};
   
Run Code Online (Sandbox Code Playgroud)

由于编译器有关的类型,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;
Run Code Online (Sandbox Code Playgroud)

是因为std::endl是一个模板化的函数,并且这个声明没有提供信息来专门化那个模板(即选择什么charT或是什么traits)。如果它不能特化 templated std::endl,它就不能推断该函数的返回类型,因此类型推导失败。