9

If I try to compile the following code I get the following compiler error (see code.) It compiles without error if std::endl is removed.

#include <iostream>
#include <sstream>
#include <utility>

namespace detail
{
    template <class T>
    void print(std::ostream& stream, const T& item)
    {
        stream << item;
    }

    template <class Head, class... Tail>
    void print(std::ostream& stream, const Head& head, Tail&&... tail)
    {
        detail::print(stream, head);
        detail::print(stream, std::forward<Tail>(tail)...);
    }
}

template <class... Args>
void print(std::ostream& stream, Args&&... args)
//note: candidate function not viable: requires 3 arguments, but 4 were provided
{
    std::stringstream ss;
    detail::print(ss, std::forward<Args>(args)...);
    stream << ss.rdbuf();
}

int main()
{
    print(std::cout, "The answer is ", 42, std::endl);
    //error: no matching function for call to 'print'
}
Chris_F
  • 4,991
  • 5
  • 33
  • 63
  • 2
    it's not that cryptic. it means no definition of `print` takes those arguments. i'm not sure where that `print` even comes from. why not just use `std::cout << "The answer is " << 42 << std::endl;` – Joseph Mark Jun 24 '14 at 03:32
  • are you asking "What is wrong with the code?", or "Why is the error message unclear?" – M.M Jun 24 '14 at 03:34
  • @sjeohp Are you sure? That sounds crazy. – Chris_F Jun 24 '14 at 03:34
  • 1
    that is standard c++ syntax for output – Joseph Mark Jun 24 '14 at 03:35
  • @MattMcNabb I am asking the former but I will take an answer to the latter as well if you have one. – Chris_F Jun 24 '14 at 03:35
  • @sjeohp I am quite aware. – Chris_F Jun 24 '14 at 03:38
  • 2
    `std::endl` is a template. – T.C. Jun 24 '14 at 03:45
  • @sjeohp It would have been better to make a new comment instead of editing your old one as I nearly missed it. What do you mean "no definition of print takes those arguments"? It is a variadic template. If the answer is obvious to you then *please* explain, that's the reason why I made this question, of course. Why do I not use cout directly? Does that matter? That doesn't change the fact that this code fails to compile and I would like to know why. – Chris_F Jun 24 '14 at 03:45

3 Answers3

11

std::endl is a function template. When it is used, its template parameters have to be explicitly specified or deduced by the compiler.

std::ostream has an overload:

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

When we use

std::cout << std::endl;

the compiler deduces the types to be used for std::endl. Since you don't have the ability to fall back on automatic type deduction when calling print, you have to be explicit about which version of std::endl you want to use.

The following should work:

print(std::cout, "The answer is ", 42, std::endl<char, std::char_traits<char>>);

Update

I used the following stripped down code to track the issue:

#include <iostream>

namespace detail
{
   template <class T>
      void print(std::ostream& stream, const T& item)
      {
         stream << item;
      }
}

int main()
{
    // detail::print(std::cout, std::endl);
    detail::print(std::cout, std::endl<char, std::char_traits<char>>);
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 2
    small nitpick, it is template type deduction, not ADL. (ADL refers to name-lookup based on argument types; however name-lookup succeeds here) – M.M Jun 24 '14 at 04:00
  • 2
    To elaborate on @Matt's point, [here](http://coliru.stacked-crooked.com/a/919c935a089efa20) `endl` is being found by ADL. It doesn't happen in your example because there is no unqualified reference to `endl` – Praetorian Jun 24 '14 at 04:08
  • @MattMcNabb and @Praetorian, I am having a hard time figuring out how the right template parameters are used for `std::endl` when used in `std::cout << std::endl`. I looked up `14.6.4.2 Candidate functions` in the standard but that didn't seem to explain it. Any pointers? – R Sahu Jun 24 '14 at 04:23
  • Praetorian can give a more specific answer; but in broad terms: it matches the overload `basic_ostream& operator<<(ios_base& (*pf)(ios_base&));` . This is not a template function so it doesn't have the same problem as OP question here in trying to deduce two sets of template parameters at once. It deduces parameters for `std::endl` that make it match that function pointer type. – M.M Jun 24 '14 at 04:35
  • I discovered this when getting to the bottom of [this compiler bug](http://stackoverflow.com/questions/23361477/wrong-overload-selected-for-stream-manipulator) – M.M Jun 24 '14 at 04:36
  • 1
    @MattMcNabb There's a [better match](http://stackoverflow.com/a/4633903/241631) for `std::cout << std::endl;`. Note that `operator<<` itself is being found by `ADL`, but not `std::endl`. – Praetorian Jun 24 '14 at 05:26
  • @RSahu: Nope just templates :D – Lightness Races in Orbit Jun 22 '15 at 23:21
  • @LightnessRacesinOrbit, that comment went over my head but I am glad you had a laugh :D. – R Sahu Jun 23 '15 at 05:25
5

I think this is because template type deduction fails if you are passing a function template. It can't deduce the parameters to instantiate endl with.

Note that the definition of endl is:

template <class charT, class traits> 
basic_ostream<charT,traits>& endl (basic_ostream<charT,traits>& os);

Simpler example:

template<class U> void func(U &u) { }

template<class T>
void print(const T &item) { }

int main()
{
print(func);    // error: matching function for call to 'print(<unresolved overloaded function type>)'
}

Your error messages come about because it tries various ways to match your function call to the parameter pack but none of them worked.

M.M
  • 138,810
  • 21
  • 208
  • 365
1

You could avoid the problem by defining a simple endl yourself (Live Demo):

constexpr struct endl_ {
    friend std::ostream& operator << (std::ostream& os, const endl_&) {
        os << '\n'; // << std::flush;
        return os;
    }
} endl;

template <class... Args>
void print(std::ostream& stream, Args&&... args)
{
    std::stringstream ss;
    std::initializer_list<int>{0, (void(ss << std::forward<Args>(args)), 0)...};
    stream << ss.rdbuf();
}

int main()
{
    print(std::cout, "The answer is ", 42, endl);
    //error: no matching function for call to 'print'
    print(std::cout, "The answer is NOT ", 13, endl);
}
Casey
  • 41,449
  • 7
  • 95
  • 125