I decided to try my own hand at a bit of Substitution Failure Is Not A Erro开发者_JAVA技巧r (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?
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.
精彩评论