LINUX.ORG.RU

Вывод типа для значений для которых нет перегрузки в std::ostream

 


1

3

Вопрос к знатокам.

Хочу сделать вывод названий типов используя typeid(T).name() для выражений которые «не умеют» выводить своё значение в std::ostream.

Пытаюсь делать вот так:

#include <iostream>
#include <typeinfo>

template <typename T, typename = std::ostream&>
struct can_be_streamed : std::false_type {};

template <typename T>
struct can_be_streamed<T, decltype(std::declval<std::ostream&>() << std::declval<T>())> : std::true_type {};

template <typename S, typename T>
typename std::enable_if<!can_be_streamed<T>::value, S&>::type operator<<(S &s, const T &t)
{
    return s << "type:" << typeid(t).name();
}

class A {};

std::ostream& operator<<(std::ostream &s, const A &)
{
    return s << "value:" /*<< ...*/;
}

class B {};

int main(void)
{
    A a;
    std::cout << a << std::endl;
    B b;
    std::cout << b << std::endl;
    return EXIT_SUCCESS;
}

Есть несколько версий компиляторов и...

gcc 9.1.0 всё Ok.

clang 7.0.1 всё Ok.

А вот gcc 6.4.0 кричит:

v1.cpp: In substitution of 'template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::error_code&) [with _CharT = char; _Traits = std::char_traits<char>]':
v1.cpp:8:66:   recursively required by substitution of 'template<class S, class T> typename std::enable_if<(! can_be_streamed<T>::value), S&>::type operator<<(S&, const T&) [with S = std::basic_ostream<char>; T = char [8]]'
v1.cpp:8:66:   required by substitution of 'template<class S, class T> typename std::enable_if<(! can_be_streamed<T>::value), S&>::type operator<<(S&, const T&) [with S = std::basic_ostream<char>; T = char [8]]'
v1.cpp:20:17:   required from here
v1.cpp:8:66: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
 struct can_be_streamed<T, decltype(std::declval<std::ostream&>() << std::declval<T>())> : std::true_type {};

Откуда рекурсия?

И что нужно подправить для gcc 6.4.0 что бы это починить?

Откуда рекурсия?

Отсюда:

  • std::enable_if<!can_be_streamed<T>::value, S&>::type
  • can_be_streamed<T, decltype(std::declval<std::ostream&>() << std::declval<T>())>

Наверное, это баг, раз в новой версии работает. Если конфликтовать ни с чем в коде не будет, можно просто убрать enable_if у operator<<. У шаблонов приоритет при разрешении перегрузки ниже чем у не шаблонов.

xaizek ★★★★★
()
Ответ на: комментарий от user0xff

Просто написать (я же указал на приоритеты перегрузок):

template <typename S, typename T>
S & operator<<(S &s, const T &t)
{
    return s << "type:" << typeid(t).name();
}
xaizek ★★★★★
()

Если я правильно понимаю, этот код не обязан работать до 17 стандарта, а он в gcc 6.4 недоделан. Можно попробовать переписть на void_t там в Notes реализация для старых стандартов

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.