测试是否存在 std::ostream 运算符<<通过 SFINAE GCC 错误?

发布于 2024-11-13 14:50:56 字数 2835 浏览 5 评论 0 原文

我决定尝试一下替换失败不是错误 (SFINAE)用于测试是否为自定义类型定义了全局运算符<< 的代码。

堆栈溢出问题SFINAE + sizeof = 检测表达式是否编译 已经通过 SFINAE 解决了对运算符 << 的测试,但我的代码略有不同,并且产生了令人费解的结果。

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

OTOH,如果我定义一个运算符<,它将会编译;< 对于一个甚至没有实例化或定义的不同类。但是,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

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

I decided to try my own hand at a bit of Substitution Failure Is Not A Error (SFINAE) code to test if the global operator<< is defined for a custom type.

The Stack Overflow question SFINAE + sizeof = detect if expression compiles already addresses testing for operator << through SFINAE, but my code is slightly different and is producing a puzzling result.

Specifically, my test code below won't even compile if I try to define operator<< for my custom type (struct A) after the test_ostr SFINAE template code -- but, from my understanding it should work fine since it's defined before any actual instantiation of the test_ostr class.

OTOH, it will compile if I define a operator<< for a different class that is not even instantiated or defined. But, then the test_ostr code fails to correctly find operator<<.

This code compiles and runs in 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;
}

Output showing the bugs:

>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

Are these compiler bugs? Am I missing something? Are the results different with a different compiler?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

溺渁∝ 2024-11-20 14:50:56

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

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

当从属名称未声明时,SFINAE 适用。

This is wrong, because operator<< is a non-dependent name. So for the case there is no operator<<, your template is ill-formed, and the compiler is at right to reject it at template definition time.

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

SFINAE applies when a dependent name turns out to be not declared.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文