为什么我必须调用operator <<作为SFINAE使用void_t的方法?

Jos*_*vin 6 c++ sfinae enable-if c++11 c++14

我正在尝试定义一个has_ostream_operator<T>SFINAE测试,以检查我是否可以cout一个给定的类型.我有它工作,但只有在我的定义中has_ostream_operator我称之为operator<<方法而不是作为中缀运算符.换句话说,这有效:

decltype(std::declval<std::ostream>().operator<<(std::declval<T>()))>

这不是:

decltype(std::declval<std::ostream>() << std::declval<T>())>

下面的测试用例(也可以参见http://coliru.stacked-crooked.com/a/d257d9d6e0f3f6d9).请注意,我包含了void_t的定义,因为我只在C++ 14上.

#include <iostream>

namespace std {

    template<class...>
    using void_t = void;

}

template<class, class = std::void_t<>>
    struct has_ostream_operator : std::false_type {};

template<class T>
struct has_ostream_operator<
    T,
    std::void_t<
        decltype(
            std::declval<std::ostream>().operator<<(std::declval<T>()))>>
    : std::true_type {};

struct Foo {};

template<class X>
    void print(
        const X& x,
        std::enable_if_t<has_ostream_operator<X>::value>* = 0)
{
    std::cout << x;
}

template<class X>
    void print(
        const X&,
        std::enable_if_t<!has_ostream_operator<X>::value>* = 0)
{
    std::cout << "(no ostream operator<< implementation)";
}

int main()
{
    print(3); // works fine
    print(Foo()); // this errors when using infix operator version
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 9

我假设你的"中缀"版本使用了这个表达式:

std::declval<std::ostream>() << std::declval<T>()
Run Code Online (Sandbox Code Playgroud)

匹配的原因Foo是因为第一部分declval<ostream>()产生了一个类型的右值ostream&&.这与非会员匹配operator<<:

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)

这种过载只会转发呼叫:

给定对输出流对象(相当于os << value)的rvalue引用,调用适当的插入运算符.

您应该直接检查它.所有重载采取ostream左值引用,所以你应该测试过:

std::declval<std::ostream&>() << std::declval<T>()
Run Code Online (Sandbox Code Playgroud)


T.C*_*.C. 6

你需要

std::declval<std::ostream&>() << std::declval<T>()
//                       ^
Run Code Online (Sandbox Code Playgroud)

std::declval<std::ostream>()是一个价值; 你正在operator<<为rvalue流击中全部重载.