枚举和静态常量成员变量在模板特征中的使用 class

enum and static const member variable usage in template trait class

我想通过查看是否提供了 operator<< 的重载来测试 class 是否可以流式传输到 ostream&。基于these posts,我尝试用C++11写了另一个版本。这是我的尝试:

#include <iostream>
#include <type_traits>

namespace TEST{
  class NotDefined{};

  template<typename T> 
  NotDefined& operator << (::std::ostream&, const T&);

  template <typename T>
  struct StreamInsertionExists {
    static std::ostream &s;
    static T const &t;
    enum { value = std::is_same<decltype(s << t), NotDefined>() };
  };
}

struct A{
  int val;
    friend ::std::ostream& operator<<(::std::ostream&, const A&);
};

::std::ostream& operator<<(::std::ostream& os, const A& a)
{
  os << a.val;
  return os;
}

struct B{};

int main() {
  std::cout << TEST::StreamInsertionExists<A>::value << std::endl;
  std::cout << TEST::StreamInsertionExists<B>::value << std::endl;

}

但是编译失败:

test_oper.cpp:40:57: error: reference to overloaded function could not be resolved; did you mean to call it?  
  std::cout << TEST::StreamInsertionExists<A>::value << std::endl;  

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/ostream:1020:1: note:  
  possible target for call  
    endl(basic_ostream<_CharT, _Traits>& __os)  

test_oper.cpp:30:17: note: candidate function not viable: no known conversion from 'TEST::NotDefined' to '::std::ostream &'  
  (aka 'basic_ostream<char> &') for 1st argument  
::std::ostream& operator<<(::std::ostream& os, const A& a)  

test_oper.cpp:15:15: note: candidate template ignored: couldn't infer template argument 'T'  
  NotDefined& operator << (::std::ostream&, const T&);  

但是,如果我替换行
enum { value = std::is_same<decltype(s << t), NotDefined>() };

static const bool value = std::is_same<decltype(s << t), NotDefined>();
然后一切编译。

为什么 enumbool 有这么大的区别?

valueStreamInsertionExists<T> 中匿名名称的枚举。当您尝试这样做时:

std::cout << StreamInsertionExists<T>::value;

编译器正在 operator<<(std::ostream&, StreamInsertionExists<T>::E) 上执行重载查找。在典型情况下,它会对 enum 进行积分提升并将其流式传输为 int。但是,您另外定义了这个运算符:

template<typename T> 
NotDefined& operator << (std::ostream&, const T&);

那是 enumint 版本更好的匹配(精确匹配与积分提升),因此它是首选。是的,它是一个函数模板,但只有在转换序列匹配时才首选非模板 - 而在这种情况下它们不匹配。

因此,这一行:

std::cout << TEST::StreamInsertionExists<A>::value << std::endl;

即:

operator<<(operator<<(std::cout, TEST::StreamInsertionExists<A>::value), std::endl);

最里面的 operator<< 调用将使用您的 NotDefined& 模板。因此,下一步是找到一个合适的函数调用:

operator<<(NotDefined&, std::endl);

并且 operator<< 没有这样的重载,因此出现错误。

如果把value改成bool,就没有这个问题了,因为一个operator<<需要bool 正好:#6。也就是说,即使 bool,你的特征仍然不正确,因为它总是 returns false。您的 NotDefined 版本实际上是 returns 一个参考,因此您必须核对一下。而且,NotDefined& 表示 未定义 ,因此您必须翻转符号:

static const bool value = !std::is_same<decltype(s << t), NotDefined&>();

然而,这特别容易出错。现在 cout << B{}; 不是编译失败而是给你一个链接器错误,而 cout << B{} << endl; 给你同样令人困惑的重载错误涉及 endl 而不是简单地声明你不能流式传输 B

你应该更喜欢这样做:

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

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

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