首页 > 解决方案 > 检查类是否可流式传输的概念

问题描述

如何实现一个概念来检查一个类型是否可以使用 std::ostream 进行流式传输?可能使用约束/ requires,但我通过谷歌找到的信息要么非常基本,要么也可以是克林贡语。

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

所以我可以使用它,例如:

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

标签: c++c++-concepts

解决方案


概念非常新。我大约 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_streamabletrait。上面的定义是 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 是一个大的。要得到足够广泛的支持以使在生产中使用概念变得可行还需要一段时间。


推荐阅读