#c #templates #c 14 #stringstream #enable-if
Вопрос:
Я разрабатываю класс инструментов с C 14, который позволяет разработчикам легко печатать все виды объектов.
Для std::map
объекта я пытаюсь разработать такую функцию:
templatelt;typename M, typename = std::enable_if_tlt; std::is_samelt;M, std::maplt;typename M::key_type, typename M::mapped_typegt;gt;::value || std::is_samelt;M, std::unordered_maplt;typename M::key_type, typename M::mapped_typegt;gt;::valuegt;gt; std::string map2String(const Mamp; mp) { std::stringstream res; for (const autoamp; element : mp) { res lt;lt; element.first lt;lt; "," lt;lt; element.second lt;lt; "|"; } return res.str(); }
Это работает так, как и ожидалось. (Кстати, C 11 тоже приемлем, я могу использовать способ замены C 11 std::enable_if_t
.)
Однако, как вы видите std::stringstream
, он был использован, а это значит, что M::key_type
и M::mapped_type
должен передаваться в потоковом режиме.
Но я не знаю, как проверить, транслируются ли они. Мне нужно что-то вроде этого:
std::enable_if_tlt;is_streamablelt;typename M::key_typegt;gt;::value
Но is_streamable
в Стандартной библиотеке его нет. Если нет is_streamable
, есть ли какой-либо другой способ сделать ошибку во время компиляции для тех типов, которые не могут быть записаны в поток при вызове моей функции?
Ответ №1:
Вот простой способ создать свой собственный признак типа в C 14:
#include lt;type_traitsgt; #include lt;utilitygt; namespace is_streamable_impl { template lt;typename T, typename Enable = voidgt; struct check : public std::false_type {}; template lt;typename Tgt; struct checklt;T, std::enable_if_tlt;std::is_samelt; decltype(std::declvallt;std::ostreamamp;gt;() lt;lt; std::declvallt;Tgt;()), std::ostreamamp;gt;::valuegt;gt; : public std::true_type {}; } template lt;typename Tgt; struct is_streamable : public std::integral_constantlt;bool, checklt;Tgt;::valuegt; {};
Эта черта проверяет , os lt;lt; val
является ли допустимым выражением типа lvalue std::ostream
, учитывая выражение os
типа std::ostream
lvalue и выражение val
с типом и категорией значений, относящееся к T
. Можно было бы написать и другие варианты точных требований.
Определение is_streamable_impl::check
, приведенное выше, на самом деле может быть достаточно хорошим в качестве прямой черты. Но это позволяет кому-то злоупотреблять им как checklt;bad_type, voidgt;::value == true
. Вы можете просто назвать это плохим использованием и не беспокоиться об этом; или этот шаблон здесь гарантирует, что дополнительный параметр шаблона скрыт.
Для вашего использования в качестве образца я бы на самом деле проверил is_streamablelt;const typename M::key_typeamp;gt;
. Это , вероятно, приведет к тому же результату, что и прямое key_type
, но это более точно для странных случаев, когда это может иметь значение.
Ответ №2:
Вы можете проверить, можно ли их использовать в качестве операнда с std::ostream
помощью for operatorlt;lt;
.
templatelt;typename M, typename = std::enable_if_tlt; std::is_samelt;M, std::maplt;typename M::key_type, typename M::mapped_typegt;gt;::value || std::is_samelt;M, std::unordered_maplt;typename M::key_type, typename M::mapped_typegt;gt;::valuegt;, typename = decltype(std::declvallt;std::ostreamgt;() lt;lt; std::declvallt;typename M::key_typegt;()), typename = decltype(std::declvallt;std::ostreamgt;() lt;lt; std::declvallt;typename M::mapped_typegt;())gt; std::string map2String(const Mamp; mp) { ... }