可变参数模板函数:没有匹配的调用函数,std :: endl

时间:2018-08-16 07:04:52

标签: c++ c++11 variadic-templates

在我的代码中,我使用 可变模板函数 进行记录。但是当我使用std::endl作为参数时,出现以下编译器错误:

  

错误:没有匹配的函数可以调用'LOG_ERROR(const char [14],   int&,)'LOG_ERROR(“ x + y的总和   =“,z,std :: endl);

     

注意:候选人:'void LOG_ERROR()'内联void LOG_ERROR(){

     

注意:候选人期望0个参数,提供3个参数

我的代码:

#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);
}

该代码在"\n"上可以正常工作,但是我很想学习为什么在std::endl上失败,以及如何解决该问题

4 个答案:

答案 0 :(得分:8)

长话短说-std::endl是函数模板,传递时无法推导出模板参数。您可以通过以下方式帮助您的编译器:

LOG_ERROR("My foo = ", foo, std::endl<char, std::char_traits<char>>);

就我所知,这是丑陋的代码,可以完美地工作。

答案 1 :(得分:7)

在有人提供更好的解决方案之前,您可以使用带有适当运算符重载的琐碎包装器:

struct EndlWrap {};

std::ostream& operator << (std::ostream& os, EndlWrap) {
   return os << std::endl;
}

应该可以这样使用:

LOG_ERROR("My foo = ", foo, EndlWrap{});

当您的日志记录目标可能是非标准流时,这是有优势的,即,std::endl的模板参数在被<<插入流中时仍然可以推导出来。

答案 2 :(得分:1)

std::endl不是字符类型或任何其他类型。它是输出流操纵器。它的返回类型是输出流。

因此,没有类型转换,您将无法通过它。请查看here

答案 3 :(得分:1)

您可以使用默认模板参数和默认函数参数来代替可变参数模板。

代码不太干净,您将不得不选择参数数量的限制,但是它将起作用:

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*/);
}

取决于编译器,此代码可能会产生难看的汇编。一种解决方案是为每个可能的参数数量写一个重载,或者对编译器特定的函数属性(always_inline等)有足够的了解