Это потому, что ostringstream
требуется базовое преобразование в ostream
, когда вы передаете его в другой шаблон, в то время как оно не нуждается ни в каком преобразовании, когда вы передаете его в шаблон, который перенаправляет в write(std::cout, ...)
.Поэтому, если вы передаете ostringstream
, он выбирает более общий шаблон, который перенаправляет поток ostring в качестве аргумента для вывода в более конкретный шаблон.Вывод ostringstream
преобразует его в void*
, который затем печатается.
Вы можете решить эту проблему с помощью is_base_of
(мне кажется, это лучше, чем использование is_convertible
).
template<typename Arg, typename... Args, typename =
typename std::enable_if<
!std::is_base_of<
std::ostream,
typename std::remove_reference<Arg>::type,
>::value>::type
>
void write(Arg&& arg, Args&&... args )
{
write( std::cout, std::forward<Arg>(arg), std::forward<Args>(args)... );
}
Мне лично не нравится использовать слишком много SFINAE в моем коде, потому что я не могу справиться с определенным уровнем угловых скобок.Поэтому я предпочитаю использовать перегрузку
template< typename Arg, typename... Args >
void write_dispatch( std::true_type, Arg&& arg, Args&&... args )
{
std::ostream& os = arg;
write( os, std::forward<Args>(args)... );
}
template< typename Arg, typename... Args >
void write_dispatch( std::false_type, Arg&& arg, Args&&... args )
{
write( std::cout, std::forward<Arg>(arg), std::forward<Args>(args)... );
}
template< typename Arg, typename... Args >
void write( Arg&& arg, Args&&... args )
{
typedef typename std::remove_reference<Arg>::type nonref_type;
write_dispatch( std::is_base_of<std::ostream, nonref_type>(),
std::forward<Arg>(arg), std::forward<Args>(args)... );
}
Таким образом, если вы вызовете его с чем-то отличным от lvalue ostream
в качестве первого аргумента, он вызовет write_dispatch
, что преобразует вызов в такойlvalue ostream
, так что ваш другой шаблон write
может продолжаться.
В заключительной ноте вы должны сказать out << std::forward<Head>(head)
, иначе вся ваша работа по использованию std::forward
на предыдущих этапах рекурсии будетдля нуля, потому что в конце концов вы бы все равно вывели как lvalues.