问题
In my code, I use variadic template functions for the logging purpose. But when I use std::endl
as parameter, I get the following compiler error:
Error: no matching function for call to 'LOG_ERROR(const char [14], int&, )' LOG_ERROR("Sum of x+y = ", z, std::endl);
note: candidate: 'void LOG_ERROR()' inline void LOG_ERROR() {
note: candidate expects 0 arguments, 3 provided
My Code:
#include <iostream>
inline void LOG_ERROR() {
std::cout << std::endl;
}
template<typename First, typename ...Rest>
void LOG_ERROR(First && first, Rest && ...rest){
std::cout << std::forward<First>(first);
LOG_ERROR(std::forward<Rest>(rest)...);
}
int main() {
int foo=40;
LOG_ERROR("My foo = ", foo, std::endl);
}
The code works fine with "\n"
but I would love to learn why it fails with std::endl
and how I can fix it
回答1:
std::endl
is not a character type or any other type. It is output stream manipulator. Its return type is output stream.
So, you can not pass it without typecasting. Please look here
回答2:
Long story short - std::endl
is function template which template arguments can't be deduced while passing. You can help Your compiler this way:
LOG_ERROR("My foo = ", foo, std::endl<char, std::char_traits<char>>);
As much as I feel this is ugly piece of code it works perfectly.
回答3:
Until someone comes with a better solution, you can use a trivial wrapper with an appropriate operator overload:
struct EndlWrap {};
std::ostream& operator << (std::ostream& os, EndlWrap) {
return os << std::endl;
}
which should be usable like this:
LOG_ERROR("My foo = ", foo, EndlWrap{});
This has an advantage when your logging destination might be a non-standard stream, i.e., the template arguments of std::endl
can still be deduced when it's <<
'd into the stream.
回答4:
You can use defaulted template parameters and defaulted function arguments instead of a variadic template.
The code is less clean and you will have to choose a limitation on the number of parameters, but it will do the job:
template<class...>
inline void LOG_ERROR_();
template<>
inline void LOG_ERROR_<>() {
std::cout << std::endl;
}
template<typename First, typename ... Rest>
void LOG_ERROR_(First && first, Rest && ...rest){
std::cout << std::forward<First>(first);
LOG_ERROR_<Rest...>(std::forward<Rest>(rest)...);
//could be cleaner with if constexpr
}
using manip_t = std::basic_ostream<char>&(*)(std::basic_ostream<char>&);
std::basic_ostream<char>& no_manip(std::basic_ostream<char>& o){
return o;
}
template<typename A=manip_t
,typename B=manip_t, typename C= manip_t
,typename D=manip_t // to be continued
>
inline void LOG_ERROR(A&& a=no_manip, B&& b=no_manip,C&& c=no_manip
,D&& d=no_manip /*to be continued*/){
LOG_ERROR_<A,B,C,D/*to be continued*/>(
std::forward<A>(a),std::forward<B>(b),std::forward<C>(c),
std::forward<D>(d)/*to be continued*/);
}
Depending on the compiler this code could produce ugly assembly. One solution is to write an overload for each possible number of argument, or have a good knowldge of compiler specific function attributes (always_inline,etc...)
来源:https://stackoverflow.com/questions/51871383/variadic-template-functions-no-matching-function-for-call-stdendl