简体   繁体   English

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

[英]Why do I have to call operator<< as a method for SFINAE to work with void_t?

I am trying to define a has_ostream_operator<T> SFINAE test for checking whether I can cout a given type. 我正在尝试定义一个has_ostream_operator<T> SFINAE测试,以检查我是否可以输出给定类型。 I have it working, but only if in my definition of has_ostream_operator I call operator<< as a method rather than as an infix operator. 我有它工作,但只有在我的has_ostream_operator定义中,我将operator<<作为方法而不是作为中缀运算符。 In other words this works: 换句话说,这有效:

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

This does not: 这不是:

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

Test case below (can also see at http://coliru.stacked-crooked.com/a/d257d9d6e0f3f6d9 ). 下面的测试用例(也可以参见http://coliru.stacked-crooked.com/a/d257d9d6e0f3f6d9 )。 Note that I included a definition of void_t since I'm only on C++14. 请注意,我包含了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;
}

I'm assuming your "infix" version used this expression: 我假设你的“中缀”版本使用了这个表达式:

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

The reason that matches for Foo is because the first part, declval<ostream>() produces an rvalue of type ostream&& . 匹配Foo的原因是因为第一部分, declval<ostream>()产生一个类型为ostream&&的右值。 This matches a non-member operator<< : 这匹配非成员operator<<

template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os, 
                                            const T& value );

That overload simply forwards the call along: 这种过载只会转发呼叫:

Calls the appropriate insertion operator, given an rvalue reference to an output stream object (equivalent to os << value ). 给定对输出流对象的rvalue引用(相当于os << value ),调用适当的插入运算符。

You should instead check for that directly. 您应该直接检查。 All the overloads take an ostream by lvalue reference, so you should test that too: 所有重载都通过左值引用获取ostream ,因此您也应该测试它:

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

You need 你需要

std::declval<std::ostream&>() << std::declval<T>()
//                       ^

std::declval<std::ostream>() is an rvalue; std::declval<std::ostream>()是一个右值; you are hitting the catch-all operator<< overload for rvalue streams. 你正在使用catch-all operator<< over rvalue streams。

If you use infix notation, the rvalue stream inserter is found, since declval returns rvalues per se; 如果使用中缀表示法,则会找到右值流插入器,因为declval本身declval返回rvalues; [ostream.rvalue]: [ostream.rvalue]:

template <class charT, class traits, class T>
basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x);

This overload currently accepts all arguments for x . 此重载当前接受x所有参数。 I've submitted LWG #2534 , which, if resolved accordingly, will make your initial code work as expected. 我已经提交了LWG #2534 ,如果得到相应的解决,将使您的初始代码按预期工作。

A temporary workaround is to make declval return an lvalue reference, ie adjust the template argument to one: 临时解决方法是使declval返回左值引用, declval模板参数调整为:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM