如何检查变量是否可以写入流

How to check if a variable can be written into a stream

我正在使用 C++14 开发一个工具 class,它允许开发人员轻松打印各种对象。

对于std::map对象,我尝试开发这样一个函数:

template<typename M, typename = std::enable_if_t<
                                std::is_same<M, std::map<typename M::key_type, typename M::mapped_type>>::value ||
                                std::is_same<M, std::unordered_map<typename M::key_type, typename M::mapped_type>>::value>>
std::string map2String(const M& mp) {
    std::stringstream res;
    for (const auto& element : mp) {
        res << element.first << "," << element.second << "|";
    }
    return res.str();
}

它按预期工作。 (顺便说一句,C++11也可以,我可以用C++11的方式来代替std::enable_if_t。)

但是,如您所见,std::stringstream 已被使用,这意味着 M::key_typeM::mapped_type 必须流式传输。

但我不知道如何检查它们是否被流式传输。我需要这样的东西:

std::enable_if_t<is_streamable<typename M::key_type>>::value

但是标准库中没有is_streamable。如果没有is_streamable,有没有其他方法可以让那些在调用我的函数时无法写入流的类型产生编译时错误?

您可以检查它们是否可以用作 std::ostream 的操作数 for operator<<

template<typename M, typename = std::enable_if_t<
                                std::is_same<M, std::map<typename M::key_type, typename M::mapped_type>>::value ||
                                std::is_same<M, std::unordered_map<typename M::key_type, typename M::mapped_type>>::value>,
                     typename = decltype(std::declval<std::ostream>() << std::declval<typename M::key_type>()),
                     typename = decltype(std::declval<std::ostream>() << std::declval<typename M::mapped_type>())>
std::string map2String(const M& mp) {
    ...
}

这是在 C++14 中创建您自己的类型特征的简单方法:

#include <type_traits>
#include <utility>

namespace is_streamable_impl {
    template <typename T, typename Enable = void>
    struct check : public std::false_type {};

    template <typename T>
    struct check<T,
        std::enable_if_t<std::is_same<
            decltype(std::declval<std::ostream&>() << std::declval<T>()),
            std::ostream&>::value>>
    : public std::true_type {};
}

template <typename T>
struct is_streamable : public std::integral_constant<bool, check<T>::value> {};

此特征检查 os << val 是类型 std::ostream 的有效左值表达式,给定类型 std::ostream 的左值表达式 os 和表达式 val 类型和值类别与 T 相关。也可以写出关于确切要求的其他变体。

上面is_streamable_impl::check的定义实际上可能直接作为特征就足够好了。但它确实允许有人将其滥用为 check<bad_type, void>::value == true。您可以将其称为不良用法而不必担心;或者这里的这个模式确保额外的模板参数被隐藏起来。

为了您的示例使用,我实际上会检查 is_streamable<const typename M::key_type&>。这可能会得到与直接 key_type 相同的结果,但对于可能很重要的奇怪情况,它会更精确。