I'm compiling with C++17 with code similar to this sample:
#include <iostream>
#include <iterator>
class Foo {};
template <typename... Args,
typename ostream_type = ::std::basic_ostream<Args...>,
typename ostreambuf_iterator_type = ::std::ostreambuf_iterator<Args...>>
ostream_type &operator<<(ostream_type &os, const ::Foo f) {
// Do ostreambuf_iterator_type stuff with 'f' here...
return os;
}
int main() {
::Foo f;
::std::cout << f;
return 0;
}
What I've discovered is template type deduction fails when I apply Args...
to the template parameter list of both ostream_type
and ostreambuf_iterator_type
, but it would be fine if I assigned the char_type
and the traits_type
from the ostream_type
.
typename ostreambuf_iterator_type = ::std::ostreambuf_iterator<typename ostream_type::char_type, typename ostream_type::traits_type>>
Why is this, when the template parameters to ::std::basic_ostream
and ::std::ostreambuf_iterator
are the same?
Template argument deduction tries to deduce ostream_type
from the function argument. In doing so it is not bound by the default argument you provided. Rather the default argument is simply ignored.
ostream_type
will be deduced to std::basic_ostream<char>
.
Then there is nothing left depending on Args
in the function parameters and the parameter pack is deduced to be empty.
The empty Args
is then expanded into the default argument ostreambuf_iterator_type = ::std::ostreambuf_iterator<Args...>
, which fails because std::ostreambuf_iterator
needs at least one template argument.
If you want Args
to be deduced as the template arguments to the std::basic_ofstream
passed to your function, you need to constraint template argument deduction in the parameter:
template <typename... Args,
typename ostreambuf_iterator_type = ::std::ostreambuf_iterator<Args...>>
auto &operator<<(::std::basic_ostream<Args...> &os, const Foo f) {
// Do ostreambuf_iterator_type stuff with 'f' here...
return os;
}
Now the deduction has to deduce Args
as the template arguments of os
.