如何检测类型是否可以流式传输到std :: ostream?

gig*_*tes 31 c++ templates iostream template-meta-programming c++11

我正在尝试编写一个类型特征来检测一个类型是否重载了适合用于输出流的operator <<().

我错过了一些东西,因为我总是认为一个简单的空类没有操作符.

这里的代码:

template<typename S, typename T>
class is_streamable
{
    template<typename SS, typename TT>
    static auto test(SS&& s, TT&& t)
    -> decltype(std::forward<SS>(s) << std::forward<TT>(t));

    struct dummy_t {};
    static dummy_t test(...);

    using return_type = decltype(test(std::declval<S>(), std::declval<T>()));

public:
    static const bool value = !std::is_same<return_type, dummy_t>::value;
};

class C {};

int main() {
    std::cout << is_streamable<std::stringstream, C>::value << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

1
Run Code Online (Sandbox Code Playgroud)

这是在ideone:https://ideone.com/ikSBoT

我究竟做错了什么?

jro*_*rok 39

这显然是这个过载operator<<的踩踏你的方式并使traling返回类型中的表达式有效:

template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os,
                                            const T& value );
Run Code Online (Sandbox Code Playgroud)

请参阅此参考页面上的(3).这是一个简单的转发器(调用os << value),它是在C++ 11中添加的,允许插入rvalue-streams,因为它们不会绑定到带左值引用的重载.

所以,问题是std::declval<SS>()返回一个右值引用并且这个重载启动.调用本身是格式良好的,但是因为函数本身没有实例化,所以即使值不可流化也不会出错.

如果您明确要求左值引用,则可以回避这个问题:std::declval<SS&>().

我还建议稍微不同的实现,而不传递流和值test.你可以declval直接在里面使用decltype.与逗号运算符一起,它看起来像这样:

#include <type_traits>
#include <utility>
#include <iostream>
#include <sstream>

template<typename S, typename T>
class is_streamable
{
    template<typename SS, typename TT>
    static auto test(int)
    -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );

    template<typename, typename>
    static auto test(...) -> std::false_type;

public:
    static const bool value = decltype(test<S,T>(0))::value;
};

class C {};

int main() {
    std::cout << is_streamable<std::stringstream, C>::value << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)


小智 6

将值传递给需要左值的函数(即TheThruth(const bool& t))时,jrok的答案会导致链接错误。所以现在在C ++ 17中,我们有了template void_t。基于CPPReference上的示例,我编写并测试了以下内容:

#include <iostream>
#include <typeinfo>

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

template<typename S, typename T>
struct is_to_stream_writable<S, T,
        std::void_t<  decltype( std::declval<S&>()<<std::declval<T>() )  > >
: std::true_type {};


class Foo
{
    public:
    Foo(){}
};

void TheTruth(const bool& t)
{
    std::cout<< t<< std::endl;
}

int main() {
    std::cout<< is_to_stream_writable<std::ostream,int>::value <<std::endl;
    std::cout<< is_to_stream_writable<std::ostream,Foo>::value <<std::endl;
    TheTruth( is_to_stream_writable<std::ostream,int>::value  );

}
Run Code Online (Sandbox Code Playgroud)

另请注意,该名称is_to_stream_writable更适合operator << 并建议使用名称:is_from_stream_readablefor operator >>(欢迎使用更好的名称建议)。

该代码在g++ -std=c++1z -O0 -Wall -pedantic main.cppgcc 6.2和7.2版本以及Coliru上进行编译