10

所以,我试图写一个这样的函数:

void append_to_stream(std::ostream &stream)
{ }

template <typename T, typename... Args>
void append_to_stream(std::ostream &stream, T first, Args&&... rest)
{
  stream << first;
  append_to_stream(stream, rest...);
}

并称之为:

append_to_stream(stream, 
                 std::endl,
                 std::endl);

但这不起作用。我收到一个错误,提示函数“参数太多”。我已经把它缩小到我知道std::endl是有罪的程度——可能是因为它是一个函数。我设法通过声明一个名为的结构endl并为它定义<<operator它来“解决”这个问题,以便它简单地调用std::endl. 这有效,但感觉不是特别好。不能接受 std::endl 作为模板参数吗?该功能适用​​于其他类型。

编辑:这是错误:

src/log/sinks/file_sink.cpp:62:21: error: too many arguments to function ‘void log::sinks::append_to_stream(std::string&, Args&& ...) [with Args = {}, std::string = std::basic_string<char>]’

更新

试图让编译器推断出正确的模板参数@MooingDuck 建议可以使用以下形式的函数:

  template<class e, class t, class a> 
  basic_ostream<e,t>&(*)(basic_ostream<e,t>&os) get_endl(basic_string<e,t,a>& s) 
  {
return std::endl<e,t>;
  }

但是,这不会编译。

错误:

src/log/sinks/file_sink.cpp:42:28: error: expected unqualified-id before ‘)’ token
src/log/sinks/file_sink.cpp:42:53: error: expected initializer before ‘get_endl’

任何想法为什么?为了编译这个,我添加了using namespace std;

4

2 回答 2

15

std::endl是模板,而不是函数,编译器无法解析endl使用哪个。

尝试:

append_to_stream(std::cout,
             std::endl<char, std::char_traits<char>>,
             std::endl<char, std::char_traits<char>>);

或者,MooingDuck 的解决方案(已更正):

template<class e, class t, class a> //string version
std::basic_ostream<e, t>& (*get_endl(const std::basic_string<e, t, a>&))
    (std::basic_ostream<e, t>& )
{ return std::endl<e,t>; } 

template<class e, class t> //stream version
std::basic_ostream<e, t>& (*get_endl(const std::basic_ostream<e, t>&))
    (std::basic_ostream<e, t>& )
{ return std::endl<e,t>; }

int main () {
  std::ostream& stream = std::cout;
  append_to_stream(stream,
                 get_endl(stream),
                 get_endl(stream));
}

这是 get_endl 解决方案,由 C++11decltype功能简化:

template<class e, class t, class a> //string version
auto get_endl(const std::basic_string<e, t, a>&)
  -> decltype(&std::endl<e,t>)
{ return std::endl<e,t>; }

template<class e, class t> //stream version
auto get_endl(const std::basic_ostream<e,t>&)
  -> decltype(&std::endl<e,t>)
{ return std::endl<e,t>; }

int main () {
  std::ostream& stream = std::cout;
  append_to_stream(stream,
                 get_endl(stream),
                 get_endl(stream));
}
于 2012-04-04T17:21:30.040 回答
4

通过强制转换解决重载比指定模板参数或定义一个全新的模板(!)要容易得多。

typedef std::ostream & (&omanip_t)( std::ostream & );

append_to_stream(stream, 
                 static_cast< omanip_t >( std::endl ),
                 static_cast< omanip_t >( std::endl ) );

这将适用于所有操纵器,而某些操纵器可以采用不同的模板,例如,如果用户提供。

此外,您应该通过T first完美转发或 const 引用。先转发,再传值,意义不大。此外,如果不调用std::forward, rvalue 参数将按值传递......只是遵循成语,它将是

template <typename T, typename... Args>
void append_to_stream(std::ostream &stream, T &&first, Args&&... rest)
{
  stream << std::forward< T >( first );
  append_to_stream(stream, std::forward< Args >( rest ) ... );
}

http://ideone.com/cw6Mc

于 2012-04-05T01:29:02.560 回答