问题
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'
}
回答1:
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>>);
}
回答2:
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.
回答3:
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);
}
来源:https://stackoverflow.com/questions/24377892/why-is-stdendl-generating-this-cryptic-error-message