怎么" std :: cout<<的std :: ENDL;"编译?

时间:2016-10-29 18:13:50

标签: c++ templates overload-resolution iomanip

大多数IO stream manipulators是具有以下签名的常规函数​​:

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

然而,一些操纵者(包括最常用的操纵者 - std::endlstd::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<<指的是哪个功能?
  3. 模板参数推导(1)应该在重载决策(2)之前发生,但似乎(至少某些部分)(2)需要执行才能使(1)成功。

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

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

    后续问题: How does overload resolution work when an argument is an overloaded function?

4 个答案:

答案 0 :(得分: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_ostreamstd::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;

答案 1 :(得分:5)

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

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

答案 2 :(得分:4)

给出表单

的语句表达式
 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类型的信息,因此它知道charTtraits要专门化前面的模板。

以上原因导致std::endl表达式std::cout << std::endl与特定std::basic_ostream<charT, traits>& endl( std::basic_ostream<charT, traits>&)匹配。

类型扣除的原因在

中无效
 auto myendl = std::endl;

是因为std::endl是模板化函数,并且此声明不提供专门化该模板的信息(即选择charTtraits是什么)。如果它无法专门化模板std::endl,则无法推断该函数的返回类型,因此类型推导失败。

答案 3 :(得分:-3)

你需要把&lt;&lt;在结束之前。它是ofstream的成员:

$url = 'https://query.yahooapis.com/v1/public/yql?q=%20select%20*%20from%20weather.forecast%20where%20woeid%20in%20(SELECT%20woeid%20FROM%20geo.places%20WHERE%20text=%22(28.56,77.32)%22)%20and%20u=%27c%27&format=json';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1);

$output = curl_exec($ch);

endl就像&#34; / n&#34;跳过该行,所以你需要一个cout行跳过