Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is std::endl generating this cryptic error message?

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'
}
like image 427
Chris_F Avatar asked Jun 24 '14 03:06

Chris_F


3 Answers

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>>);
}
like image 84
R Sahu Avatar answered Oct 17 '22 16:10

R Sahu


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.

like image 5
M.M Avatar answered Oct 17 '22 16:10

M.M


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);
}
like image 1
Casey Avatar answered Oct 17 '22 16:10

Casey