Концепция, чтобы проверить, является ли класс потоком - PullRequest
2 голосов
/ 14 апреля 2020

Как я могу реализовать концепцию, чтобы проверить, является ли тип потоковым, используя std :: ostream? Возможно использование ограничений / requires, но информация, которую я нахожу через Google, либо очень элементарна, либо может быть просто клингоном.

template<typename T> concept bool can_ostream()
{
    return check_if_operator<<(..)_is_in_T;  or something like this
}

, поэтому я могу использовать ее, например:

template<can_ostream T> struct X { ... }

Ответы [ 2 ]

1 голос
/ 16 апреля 2020

Правильный синтаксис для нужной вам концепции гласит:

template <typename T>
concept Streamable = requires(std::ostream os, T value) {
    { os << value };
  };

По отношению к понятиям clang отстает от g cc. Поэтому я рекомендую использовать g cc. Вы можете запустить следующий код онлайн :

#include <iostream>

template <typename T>
concept Streamable = requires(std::ostream os, T value) {
    { os << value };
  };

void call(Streamable auto obj)
{
    std::ostream os(nullptr);
    os.rdbuf(std::cout.rdbuf());
    os << obj << std::endl;
    //do stuff with os ...
}

int main() 
{
    call("Hi");
    call(3.14159);
    return 0;
}

Смотрите также этот пост для аналогичного вопроса .

1 голос
/ 14 апреля 2020

Концепции очень новые. Я примерно на 90% уверен, что следующий способ является правильным, но я не смог заставить его скомпилировать на clang:

template <typename T>
concept Streamable = 
  requires(std::ostream &os, T value) {
    { os << value } -> std::convertible_to<std::ostream &>;
  };

Чтобы обойти ограничения clang, вы может сделать это:

template <typename T>
concept Stream = std::is_convertible_v<T, std::ostream &>;

template <typename T>
concept Streamable =
  requires(std::ostream &os, T value) {
    { os << value } -> Stream;
  };

Другой подход - определить черту без понятий, а затем определить понятие в терминах черты. Но затем вы жертвуете одним из преимуществ концептов - сообщениями об ошибках.

// This is how we used to do things back in my day

template <typename T, typename = void>
struct is_streamable : std::false_type {};

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

template <typename T>
concept Streamable = is_streamable<T>::value;

Для компиляторов, которые не поддерживают концепты, вы можете просто использовать черту is_streamable. Вышеприведенное определение - C ++ 17, но с некоторыми изменениями это можно сделать в C ++ 11.

template <typename T, typename = void>
struct is_streamable : std::false_type {};

template <typename T>
struct is_streamable<T, typename std::enable_if<
  std::is_convertible<
    decltype(std::declval<std::ostream &>() << std::declval<T>()),
    std::ostream &
  >::value
>::type> : std::true_type {};

Понятия - просто сахар. Понятия дают вам лучшие сообщения об ошибках и их легче написать, чем описанные выше шаблоны. AFAIK, они не позволяют тебе делать то, что ты не мог сделать в C ++ 17.


Я понял, что эта черта может быть немного упрощена. Я снова использую C ++ 11 для обеспечения переносимости.

template <typename T, typename = void>
struct is_streamable : std::false_type {};

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

Поскольку я снова редактирую ответ, я скажу, что понятия - просто сахар! C ++ 20 большой. Пройдет некоторое время, прежде чем поддержка станет достаточно распространенной, чтобы сделать возможным использование концепций в производстве.

...