模板特征类中的枚举和静态const成员变量用法

Hsu*_*Hau 5 c++ templates overload-resolution c++11

我想ostream&通过查看是否提供了重载来测试类是否可以流式传输operator<<.根据这些 帖子,我尝试使用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;

}
Run Code Online (Sandbox Code Playgroud)

但这无法编译:

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&);  
Run Code Online (Sandbox Code Playgroud)

但是,如果我更换线路
enum { value = std::is_same<decltype(s << t), NotDefined>() };
,
static const bool value = std::is_same<decltype(s << t), NotDefined>();
那么一切都会编译.

为什么enumbool?之间存在这样的差异?

Bar*_*rry 4

value是 中匿名名称的枚举StreamInsertionExists<T>。当你尝试这样做时:

std::cout << StreamInsertionExists<T>::value;
Run Code Online (Sandbox Code Playgroud)

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

template<typename T> 
NotDefined& operator << (std::ostream&, const T&);
Run Code Online (Sandbox Code Playgroud)

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

因此,这一行:

std::cout << TEST::StreamInsertionExists<A>::value << std::endl;
Run Code Online (Sandbox Code Playgroud)

这是:

operator<<(operator<<(std::cout, TEST::StreamInsertionExists<A>::value), std::endl);
Run Code Online (Sandbox Code Playgroud)

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

operator<<(NotDefined&, std::endl);
Run Code Online (Sandbox Code Playgroud)

并且不存在这样的过载,因此会出现operator<<错误。

如果你改为valuebe bool,就不存在这样的问题,因为有一个恰好operator<<需要bool#6。也就是说,即使有bool,你的特质仍然不正确,因为它总是返回false。您的NotDefined版本实际上返回了一个引用,因此您必须对此进行检查。而且,NotDefined&意思没有定义,所以你必须翻转符号:

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

然而,这特别容易出错。现在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 { };
Run Code Online (Sandbox Code Playgroud)