[英]How does “std::cout << std::endl;” compile?
大多數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
指的是std::endl<CharT, Traits>()
哪個專業化? operator<<
指的是哪個功能? 模板自變量推導(1)應該在重載解決方案(2)之前發生,但是似乎(2)的至少一部分必須執行才能使(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>>
,又名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;
因為basic_ostream
具有operator<<
的模板重載,所以basic_ostream
需要這樣的函數指針:
basic_ostream<charT, traits>& operator<<(basic_ios<charT, traits>& (*pf)(basic_ios<charT, traits>&));
給定一個形式的語句表達
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
模板的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
,則無法推斷該函數的返回類型,因此類型推導失敗。
您需要將<<放在endl之前。 它是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行來跳過
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.