Проверить наличие оператора std :: ostream << через ошибку SFINAE GCC? - PullRequest
3 голосов
/ 04 июня 2011

Я решил попробовать свои силы с кодом Ошибка замещения не является ошибкой (SFINAE), чтобы проверить, определен ли глобальный operator<< для пользовательского типа.

Вопрос переполнения стека SFINAE + sizeof = определить, компилируется ли выражение , уже посвящено тестированию для оператора << через SFINAE, но мой код немного отличается и дает удивительный результат.

В частности, мой тестовый код ниже даже не скомпилируется, если я попытаюсь определить operator<< для моего пользовательского типа (структура A) после кода шаблона test_ostr SFINAE - но,насколько я понимаю, он должен работать нормально, поскольку он определен до любого фактического создания экземпляра класса 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 Ответ

3 голосов
/ 04 июня 2011

Это неправильно, потому что operator<< - это независимое имя.Таким образом, для случая нет operator<<, ваш шаблон плохо сформирован, и компилятор вправе отклонить его во время определения шаблона.

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

SFINAE применяется, когда зависимое имя оказывается не объявленным.

...