在可变参数模板函数中重载 ostream
我有一个可变参数函数,我想在第一个参数类型上重载。
void write( void ) { }
void write( std::ostream& ) { }
template< typename Head, typename... Rest >
void write( std::ostream& out, Head&& head, Rest&&... rest )
{
out << head;
write( out, std::forward<Rest>(rest)... );
}
template< typename... Args >
void write( Args&&... args )
{
write( std::cout, std::forward<Args>(args)... );
}
但这些功能的表现并不如预期。
write( "## to cout ##" ); // printed to stdout as expected
write( std::cerr, "## to cerr ##" ); // printed to stderr as expected
std::ostringstream oss;
write( oss, "## to string ##" ); // error here
// '0x7fff9db8## to string ##' is printed to stdout!
这是怎么回事?
为什么重载解析没有选择我想要的函数?
有没有一种方法可以在不进行大量元编程的情况下做到这一点? (我可以使用 std::is_convertible 解决这个问题,但解决方案比我上面显示的简单代码大得多)。
I have a variadic function that I want to overload on the first parameter type.
void write( void ) { }
void write( std::ostream& ) { }
template< typename Head, typename... Rest >
void write( std::ostream& out, Head&& head, Rest&&... rest )
{
out << head;
write( out, std::forward<Rest>(rest)... );
}
template< typename... Args >
void write( Args&&... args )
{
write( std::cout, std::forward<Args>(args)... );
}
But these functions don't behave as expected.
write( "## to cout ##" ); // printed to stdout as expected
write( std::cerr, "## to cerr ##" ); // printed to stderr as expected
std::ostringstream oss;
write( oss, "## to string ##" ); // error here
// '0x7fff9db8## to string ##' is printed to stdout!
What's going on here?
Why isn't overload resolution picking the function I want?
Is there a way to do this without lots of metaprogramming? (I was able to work around it with std::is_convertible
but the solution was much larger than the simple code I've shown above).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是因为,当您将
ostringstream
传递给其他模板时,需要将其基本转换为ostream
,而当您将其传递给转发到的模板时,它不需要任何转换。write(std::cout, ...)
。因此,如果您传递 ostringstream,它会选择更通用的模板,该模板将 ostringstream 作为参数转发以输出到更具体的模板。输出ostringstream
会将其转换为void*
,然后进行打印。您可以使用
is_base_of
解决这个问题(我感觉比使用is_convertible
更好)。我个人不喜欢在代码中使用太多 SFINAE,因为我无法处理一定程度的尖括号。所以我喜欢使用重载
这种方式,如果你用
ostream
的左值以外的东西作为第一个参数来调用它,它会调用write_dispatch
,这会将调用转换为这样ostream
的左值,以便您的其他write
模板可以继续。最后一点,你应该说
out << std::forward(head)
,否则您在前面的递归步骤中使用std::forward
的所有工作都将毫无意义,因为最终您会将所有内容输出为无论如何,左值。That's because
ostringstream
needs a base conversion toostream
when you pass it to the other template, while it doesn't need any conversion when you pass it to the template that forwards towrite(std::cout, ...)
. So if you passostringstream
, it selects the more generic template which forwards the ostringstream as an argument to output to the more specific template. Outputting theostringstream
converts it to avoid*
, which is then printed.You can solve this with
is_base_of
(just feels better to me than usingis_convertible
).I personally dislike using too much SFINAE in my code, because I can't cope with a certain level of angle brackets. So I like to use overloading
This way, if you call it with something other than lvalue of
ostream
as first argument, it will callwrite_dispatch
, which will transform the call into such an lvalue ofostream
, so that your otherwrite
template can continue.On a final note, you should say
out << std::forward<Head>(head)
, otherwise all your work to usestd::forward
in previous recursion steps would be for naught, because in the end you would output everything as lvalues anyway.