为什么非专业模板胜过部分专业模板?

问题描述

我正在尝试创建一个模板化的 can_stream 结构,它继承自 std::false_typestd::true_type 取决于是否为 operator<< 类型定义了 T

#include <iostream>

struct S {
    int i;
};

std::ostream& operator<< (std::ostream& os,S const& s) {
    os << s.i << std::endl;
    return os;
};

struct R {
    int i;
};

template<
    typename T,typename Enable = void
> struct can_stream : std::false_type {
};

template<
    typename T
> struct can_stream< T,decltype( operator<<( std::declval< std::ostream& >(),std::declval< T const& >())) > : std::true_type {
};

int main() {
    std::cout << can_stream< int >::value << std::endl;
    std::cout << can_stream<  S  >::value << std::endl;
    std::cout << can_stream<  R  >::value << std::endl;
}

我以为上面的程序会产生

  1
  1
  0

因为:

  • 对于 <<int 都存在运算符 S(因此 decltype(...) 是结构良好的)。
  • 部分专用模板比非专用模板更匹配。

然而,它产生:

  0
  0
  0

为什么?

解决方法

对于 <<int 都存在运算符 S(因此 decltype(...) 是良构的)。

但是 decltype( operator<<( std::declval< std::ostream& >(),std::declval< T const& >()))std::ostream&,其中 Enable 的默认值为 void

不匹配。

你可以试试

template<
    typename T // ........................................................................................VVVVVVVVV
> struct can_stream< T,decltype( operator<<( std::declval< std::ostream& >(),std::declval< T const& >()),void() ) > : std::true_type {
};

或者,如果您可以使用 C++17,那么 std::void_t,

template<
    typename T // ......VVVVVVVVVVVV
> struct can_stream< T,std::void_t<decltype( operator<<( std::declval< std::ostream& >(),std::declval< T const& >()))> > : std::true_type {
};

这解决了 S 的问题,因为 S 有一个 operator<<() 函数。但不适用于 int,因为对于 int,运算符未定义为函数。所以,对于int,你要模拟使用

template<
    typename T // ...........................................................VVVV
> struct can_stream< T,std::void_t<decltype( std::declval< std::ostream& >() << std::declval< T const& >() )> > : std::true_type {
};

如果您更喜欢检查函数 operator<<() 的存在(但这不适用于 int 和其他带有隐式运算符 << 的类型),或者如果运算符 {{ 1}} 是具体可用的。