Как проверить, можно ли записать переменную в поток

#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) {  ... }