Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
234 views
in Technique[技术] by (71.8m points)

c++ - How does "std::cout << std::endl;" compile?

Most IO stream manipulators are regular functions with the following signature:

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:

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; 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. 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?
  2. Which function does the operator<< refer to?

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.


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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The operator<< in question is a member of 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. So the member function of cout looks like

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? 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;

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...