Если адрес функции не может быть разрешен во время дедукции, это SFINAE или ошибка компилятора?

В С++ 0x правила SFINAE были упрощены, так что любое недопустимое выражение или тип, который встречается в "непосредственном контексте" вывода, не приводит к ошибке компилятора, а скорее к отказу дедукции (SFINAE).

Мой вопрос таков:
Если я беру адрес перегруженной функции и не может быть разрешен, это ошибка в непосредственном контексте дедукции?
(т.е. это жесткая ошибка или SFINAE, если она не может быть разрешена)?

Вот пример кода:

struct X
{
  // template<class T> T* foo(T,T); // lets not over-complicate things for now
  void foo(char);
  void foo(int);
};


template<class U> struct S
{
  template<int> struct size_map 
  { typedef int type; };


// here is where we take the address of a possibly overloaded function
  template<class T> void f(T, 
      typename size_map<sizeof(&U::foo)>::type* = 0); 


  void f(...);
};

int main()
{
  S<X> s;

// should this cause a compiler error because 'auto T = &X::foo' is invalid?
  s.f(3);  

}

Gcc 4.5 утверждает, что это ошибка компилятора, и clang выплевывает нарушение утверждения.

Вот еще несколько связанных вопросов:

Указывает ли FCD-С++ 0x, что должно произойти здесь?
Неправильно ли компиляторы отклоняют этот код?
Нужно ли определять "непосредственный контекст" дедукции немного лучше?

Спасибо!

Ответ 1

template<class T> void f(T, 
    typename size_map<sizeof(&U::foo)>::type* = 0); 

Это не работает, потому что U не участвует в выводе. В то время как U является зависимым типом, во время вычитания для f он обрабатывается как фиксированный тип, записанный с независящим именем. Вам нужно добавить его в список параметров f

/* fortunately, default arguments are allowed for 
 * function templates by C++0x */
template<class T, class U1 = U> void f(T, 
    typename size_map<sizeof(&U1::foo)>::type* = 0); 

Итак, в вашем случае, потому что U::foo не зависит от параметров самого f, вы получаете сообщение об ошибке, неявно создавая экземпляр S<X> (попробуйте прокомментировать вызов, и он все равно должен потерпеть неудачу). FCD говорит в 14.7.1/1

Неявное инстанцирование специализации шаблона шаблона вызывает неявное создание объявлений, но не определений или аргументов по умолчанию, функций-членов класса, классов-членов, статических элементов данных и шаблонов-членов;

То есть, если вы неявно создаете экземпляр S<X>, будет объявлено следующее выражение шаблона функции

template<class T> void S<X>::f(T, 
  typename size_map<sizeof(&X::foo)>::type* = 0); 

Анализ этого объявления шаблона затем обнаружит, что он не может разрешить ссылку на X::foo и ошибку. Если вы добавите U1, объявление шаблона еще не попытается решить ссылку на U1::foo (так как U1 является параметром f) и, таким образом, останется в силе и SFINAE, когда f будет пытаться называться.