测试std :: ostream运算符的存在<<通过SFINAE GCC bug?

时间:2011-06-04 09:09:49

标签: c++ templates gcc metaprogramming sfinae

我决定亲自尝试一下Substitution Failure Is Not A Error(SFINAE)代码来测试是否为自定义类型定义了全局operator<<

Stack Overflow问题 SFINAE + sizeof = detect if expression compiles 已经通过SFINAE解决了对运营商<<的测试问题,但我的代码略有不同,并且产生了令人费解的结果。

具体来说,如果我尝试在 operator<< SFINAE模板代码之后为我的自定义类型(结构A)定义test_ostr,我的测试代码甚至都无法编译 - - 但是,根据我的理解,它应该可以正常工作,因为在任何实际的test_ostr类实例化之前定义了

OTOH,如果我为一个甚至没有实例化或定义的不同的类定义operator<<,它编译。但是,test_ostr代码无法正确找到operator<<

此代码在GCC 4.4.3中编译并运行:

//#define BUG 1 // Uncomment and the program will not compile in GCC 4.4.3
//#define BUG 2 // Uncomment and the program will compile, but produces an incorrect result, claiming operator<< is not defined for A.

#include <iostream>

struct A{};
struct B{};

// If BUG is #defined, the operator<< for struct A will be defined AFTER the test_ostr code
// and if BUG <=1, then GCC 4.4.3 will not compile with the error:
// sfinae_bug.cpp:28: error: template argument 2 is invalid
#ifdef BUG
// if BUG > 1, defining the opertor << for *C*, an un-defined type, will make GCC  magically compile!?
#  if BUG > 1
  struct C;
  std::ostream& operator<<(std::ostream&, const C&);
#  endif
#endif

#ifndef BUG
  std::ostream& operator<<(std::ostream& ostr, const A&) { return ostr; };
#endif

template<class T>
struct test_ostr
{
  template <class U, std::ostream& (*)(std::ostream&, const U&) >
  struct ostrfn;
  template<class U>
  static short sfinae(ostrfn<U, &operator<< >*);
  template<class U>
  static char  sfinae(...);
  enum { VALUE = sizeof(sfinae<T>(0)) - 1 };
};

#ifdef BUG
  std::ostream& operator<<(std::ostream& ostr, const A&) { return ostr; };
#endif

int main(void)
{
  std::cout << "std::ostream defined for A: " << int(test_ostr<A>::VALUE) << std::endl;
  std::cout << "std::ostream defined for B: " << int(test_ostr<B>::VALUE) << std::endl;
  return 0;
}

显示错误的输出:

>c++ sfinae_bug.cpp && ./a.out 
std::ostream defined for A: 1
std::ostream defined for B: 0

>c++ -DBUG sfinae_bug.cpp && ./a.out 
sfinae_bug.cpp:28: error: template argument 2 is invalid

>c++ -DBUG=2 sfinae_bug.cpp && ./a.out 
std::ostream defined for A: 0
std::ostream defined for B: 0

这些编译器错误吗?我错过了什么吗?结果是否与不同的编译器不同?

1 个答案:

答案 0 :(得分:3)

这是错误的,因为operator<<是非依赖名称。因此,对于没有operator<<的情况,您的模板格式不正确,编译器有权在模板定义时拒绝它。

template<class U>
static short sfinae(ostrfn<U, &operator<< >*);

当依赖名称未声明时,SFINAE适用。