dd:ostream的解密表达式(用于SFINAE)

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (64)

我正在尝试创建一个类型特性类来确定一个特定类型T可以通过<<a的运算符std::ostream我使用的是一种简单的SFINAE技术。

最后,我试图对替换失败进行评估的表达式是:

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

我的期望是,给定一个实例t类型T和一个std::ostream实例os,如果表达式os << t如果格式不正确,则应发生替换失败。

但显然,无论哪种类型,替代失败都不会在这里发生。T即使我只是宣布typedef使用上述decltype表达式,在SFINAE上下文之外,它很高兴地编译,即使T不能与std::ostream

例如:

struct Foo  { };

int main()
{
    // This compiles fine using GCC 4.9.2
    //
    typedef decltype(
        std::declval<std::ostream>() << std::declval<Foo>()
    ) foo_type;
}

上面的代码将使用gcc 4.9.2进行良好的编译,这与我预期的不同,因为<<运算符未重载以处理类型。Foo当然,如果我说:

std::cout << Foo();

我得到一个编译器错误。那么为什么decltype上面的表达式甚至可以编译吗?

提问于
用户回答回答于

C+11添加以下内容operator<<超载:

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

这将转发到标准插入运算符,后者不能将rvalue引用绑定到std::ostream因为他们接受非参照。自std::declval<std::ostream>回报std::ostream&&,选择此重载,然后由于非常允许的接口(即,如果没有有效的基础插入运算符,这不是SFINAEdout),则decltype说明有效。

简单的解决办法是使用std::declval<std::ostream&>()。这将返回一个std::ostream&,因此模板重载将不会由您选择。decltype要编译,需要说明符和正常的插入操作符重载:

typedef decltype(
    std::declval<std::ostream&>() << std::declval<Foo>()
) foo_type;

clang输出如下:

main.cpp:8:39: error: invalid operands to binary expression ('std::basic_ostream<char>' and 'Foo')
        std::declval<std::ostream&>() << std::declval<Foo>()
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~

下面是一个简单的例子,它展示了同样的问题:

#include <string>

void foo (int&,int){}
void foo (int&,float){}

template <typename T>
void foo (int&& a, T b) {
    foo(a, b);
}

int main()
{
    std::string s;
    typedef decltype(foo(1,s)) foo_type;
}

以下是相关标准报价(N 4140):

必须实例化声明,因为涉及重载解析:

[temp.inst]/10:如果以涉及重载解析的方式使用函数模板或成员函数模板专门化,则显式实例化(14.8.3)。

只需要实例化声明:

[temp.over]/5:在一组候选函数中输入专门化只需要函数模板专门化的签名。因此,只需要函数模板声明就可以解析模板专门化是候选调用的调用。

并且不允许实现实例化函数体:

[temp.inst]/11:实现不应隐式实例化不需要实例化的函数模板、变量模板、成员模板、非虚拟成员函数、成员类或类模板的静态数据成员。

用户回答回答于

并不能真正回答为什么会发生这种情况,但是如果你用std::stream&详情如下:

template<typename T, typename Enable = std::ostream&>
struct can_be_streamed : std::false_type {};
template<typename T>
struct can_be_streamed<T, 
         decltype(std::declval<std::ostream&>() << std::declval<T>())> : std::true_type {};

所属标签

可能回答问题的人

  • 爸爸

    腾讯 · 客户端安全 (已认证)

    4 粉丝4 提问5 回答
  • 找虫虫

    0 粉丝0 提问5 回答
  • 不吃貓的鱼oo

    5 粉丝466 提问4 回答
  • uncle_light

    5 粉丝518 提问4 回答

扫码关注云+社区

领取腾讯云代金券