Непонимание вариативных шаблонов и вывода типа шаблона

Я компилирую C++17 с кодом, похожим на этот пример:

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

}

Я обнаружил, что определение типа шаблона не работает, когда я подаю заявку Args... в список параметров шаблона обоих ostream_type а также ostreambuf_iterator_type, но было бы хорошо, если бы я назначил char_type и traits_type из ostream_type.

typename ostreambuf_iterator_type = ::std::ostreambuf_iterator<typename ostream_type::char_type, typename ostream_type::traits_type>>

Почему это так, когда параметры шаблона ::std::basic_ostream а также ::std::ostreambuf_iterator такие же?

1 ответ

Решение

Вывод аргументов шаблона пытается вывести ostream_typeиз аргумента функции. При этом он не связан аргументом по умолчанию, который вы указали. Скорее аргумент по умолчанию просто игнорируется.

ostream_type будет выведено к std::basic_ostream<char>.

Тогда ничего не остается в зависимости от Args в параметрах функции, и пакет параметров считается пустым.

Пустой Args затем расширяется до аргумента по умолчанию ostreambuf_iterator_type = ::std::ostreambuf_iterator<Args...>, что не удается, потому что std::ostreambuf_iterator требуется хотя бы один аргумент шаблона.

Если хочешь Args выводиться в качестве аргументов шаблона для std::basic_ofstream передается в вашу функцию, вам нужно ограничить вывод аргумента шаблона в параметре:

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

Теперь дедукция должна вывести Args в качестве аргументов шаблона os.