How does "std::cout << std::endl;" compile?
Asked Answered
E

4

17

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?

Embellishment answered 29/10, 2016 at 18:13 Comment(11)
@πάνταῥεῖ This question is not a duplicate. It's not even that Dietmar Kühl's answer contains information requested in my very different question (it only states that "The compiler deduces the template arguments automatically" while I am curious how that deduction works).Embellishment
@Leon Can you make up your arguments into your question why you think it's not clearly explained in Dietmar's or the other answers there please? I'd consider to reopen your question maybe.Ayesha
@πάνταῥεῖ I already added those arguments in my edit (before the question was closed).Embellishment
Well, start with a minimal reproducible example when asking.Ayesha
coliru.stacked-crooked.com/a/5dd253353203104eAyesha
@πάνταῥεῖ So what does your copy-paste of my example prove?Embellishment
What's not answered in the dupe in particular?Ayesha
@πάνταῥεῖ This question is about C++ rules that govern overload resolution in what appear to be a quite intricate (though very frequently used) situation. The answers in the dupe don't at all address how template argument deduction works in the context of such an overloading resolution.Embellishment
Clarify that in your question then, rather than complaining in comments. It costs me just one click to reopen your question if you're willing to make the effforts.Ayesha
@πάνταῥεῖ Done.Embellishment
OK, I'll let it go now. Not completely convinced but you shave shown more efforts.Ayesha
W
12

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;
Wrung answered 29/10, 2016 at 21:46 Comment(1)
Thanks. Your answer provided hints that helped me better understand overload resolution in this case. I have posted a more abstract question: #40344163Embellishment
W
6

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).

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 ....
};
   

Since the compiler has information about the type of std::cout it knows what charT and traits are to specialise the preceeding template.

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>&).

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 (i.e. picking what charT or traits are). If it can't specialise the templated std::endl, it can't infer that function's return type, so the type deduction fails.

Whatnot answered 29/10, 2016 at 23:48 Comment(0)
A
5

Because basic_ostream has a templated overload of operator<< that expects just such a function pointer:

basic_ostream<charT, traits>& operator<<(basic_ios<charT, traits>& (*pf)(basic_ios<charT, traits>&));
Agar answered 29/10, 2016 at 18:22 Comment(1)
And how does overloading resolution work in that case (see updated question)?Embellishment
H
-3

You need to put << before the endl. It is a member of 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

Humanoid answered 29/10, 2016 at 23:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.