Подтвердить что ты не робот

В С++, почему нет возможности связать функцию члена класса шаблона с использованием типа шаблона другого класса?

Другими словами, почему это компилируется отлично:

template<typename Type>
class A{
  public:
    void f();
};

class B{
  friend void A<int>::f();
};

template<>
void A<int>::f(){
  B* var = new B();
}

Пока это не так:

template<typename Type>
class A{
  public:
    void f();
};

template<typename Type> // B is now a templated class
class B{
  friend void A<Type>::f(); // Friending is done using B templated type
};

template<>
void A<int>::f(){
  B<int>* var = new B<int>(); // var is now declared using int as its templated type
}

Для второго фрагмента кода компилятор (gcc 6.2, без специальных флагов) говорит:

main.cpp: In instantiation of ‘class B<int>’:
main.cpp:14:28:   required from here
main.cpp:9:15: error: prototype for ‘void A<int>::f()’ does not match any in class ‘A<int>’
   friend void A<Type>::f();
               ^~~~~~~
main.cpp:13:6: error: candidate is: void A<Type>::f() [with Type = int]
 void A<int>::f(){

Как я понимаю, во втором фрагменте кода при объявлении var компилятор должен проанализировать объявление класса B, замените тип, используемый в объявлении друга int, и все должно работать нормально. Что мне не хватает?

EDIT: комментарии ниже указали, что второй фрагмент кода, похоже, правильно компилируется с помощью clang и Visual С++ 2015

4b9b3361

Ответ 1

Явное создание экземпляра B<int> до его использования в A<int>::f() устраняет эту проблему. Я предполагаю, что GCC пытается выполнить неявное создание B<int> в определении A<int>::f(). Но определение A<int>::f() не завершено, и GCC "теряет" декларацию друга. Это похоже на проблему с компилятором.

template<typename Type>
class A
{
public:
    void f();
};

template<typename Type> // B is now a templated class
class B
{
    friend void A<Type>::f(); // Friending is done using B templated type
};

template
class B<int>; // <= explicit instantiation, that works

template<>
void A<int>::f()
{
    B<int>* var = new B<int>();
}

Ответ 2

Специализирующая функция template class member без специализированного целого template class - это особый случай, когда вам разрешено специализироваться на функции не template, поэтому, возможно, GCC запутано, и я не знаю причин, но так или иначе вы не можете объявить дружбу специализированному члену template <<20 > . Временным решением будет специализироваться на целом class template, чтобы это работало.

//class template A
template<typename Type>
class A{
  public:
    void f();
};

//class A<int>
template<>
class A<int>{
  public:
    void f();
};

Затем определите A<int>::f:

Для class B:

void A<int>::f(){
      B* var = new B();
      (void)(var);
}

Для template class B:

void A<int>::f(){
      B<int>* var = new B<int>();
      (void)(var);
}

Но я думаю, что Clang здесь, не должно быть никаких проблем для такого объявления друга. Вероятно, это ошибка в GCC.