检查 class 是否可流式传输的概念

Concept to check if a class is streamable

我如何实现一个概念来检查类型是否可以使用 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 { ... }

概念很新。我大约 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 中不能做的事情。


我意识到可以稍微简化一下 trait。我再次使用 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 是一个大概念。还需要一段时间才能获得足够广泛的支持,以便在生产中使用概念。

您想要的概念的正确语法如下:

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

关于概念,clang 落后于 gcc。所以我推荐使用gcc。 You can run the following code online:

#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;
}

.