Объявление друга о специализации шаблона не выполняется

Следующий код, содержащий объявление друга, не работает с указанной ошибкой (см. http://ideone.com/Kq5dy):

template<class T> void foo() {}

template<typename T>
class A {
   void foo();
   friend void foo<T>(); // error: variable or field 'foo' declared void
};

int main()
{
   foo<int>();
}

Если порядок объявления друга и объявления функции-члена поменялся местами, код компилируется без проблем (см. http://ideone.com/y3hiK):

template<class T> void foo() {}

template<typename T>
class A {
   friend void foo<T>();
   void foo();
};

int main()
{
   foo<int>();
}

Этого не происходит, если объявление друга не содержит специализации шаблона: допустимы друзья, не являющиеся шаблонами, а также друзья по шаблону. Кроме того, использование квалифицированного имени в специализации шаблона позволяет компилировать код. У меня вопрос: почему первый пример терпит неудачу? Кажется, компилятор ищет имена в области класса в точке объявления друга и только для специализации шаблона? Где в Стандарте указано такое поведение?


person Gene Bushuyev    schedule 15.12.2011    source источник


Ответы (1)


Чтобы было ясно, что именно этой функцией вы хотите быть - friend добавьте к имени функции ::, чтобы указать, что она находится в глобальном пространстве имен.

Фрагмент, который компилируется и делает то, что вы хотите:

template<class T> void foo() {}

template<typename T>
class A {
   void foo();
   friend void ::foo<T>();
};

int main()
{
   foo<int>();
}

n1905.pdf

3.4 / 9 Поиск имени

Поиск имени для имени, используемого в определении функции друга (11.4), определенной встроенным в класс, предоставляющий дружбу, должен выполняться, как описано для поиска в определениях функций-членов. Если функция друга не определена в классе, предоставляющем дружбу, поиск имени в определении функции друга должен выполняться, как описано для поиска в определениях функций-членов пространства имен.

Ваш фрагмент не может быть скомпилирован по той же причине, что и приведенный ниже код.

template<class T> void foo () {}

template<typename T>
struct A { 

  void foo (); 

  void func () {
    foo<T> (); // when performing the name lookup A::foo is
               // found before ::foo<T>
  }
};

...

14.5.3 Друзья [temp.friend] 1

Другом класса или шаблона класса может быть шаблон функции или шаблон класса, специализация шаблона функции или шаблона класса, или обычная (не шаблонная) функция или класс. Для объявления дружественной функции, не являющейся объявлением шаблона:

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

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

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

- имя должно быть неквалифицированным идентификатором, который объявляет (или повторно объявляет) обычную (не шаблонную) функцию.

person Filip Roséen - refp    schedule 15.12.2011
comment
Я упоминал, что квалификация специализации друга позволяет его компилировать. Я не ищу обходных путей, я ищу место в Стандарте, которое объясняет, почему первый пример не работает. - person Gene Bushuyev; 15.12.2011
comment
@GeneBushuyev Тогда я постараюсь найти его для вас, извините за недоразумение. - person Filip Roséen - refp; 15.12.2011
comment
Нет, ты этого не сделал. квалифицированный-идентификатор - blah::blub (или даже ::blub), поэтому жирная часть не применима. Код в OP неквалифицирован. - person Xeo; 15.12.2011
comment
Я знаю это место в стандарте, он не отвечает на вопрос, почему первый пример не работает. - person Gene Bushuyev; 15.12.2011
comment
Да, я уверен. §A.4 [gram.expr], просмотрите раздел квалифицированный-идентификатор. - person Xeo; 15.12.2011
comment
3.4 / 9 имеет дело с name used in the definition of a friend function, а не с именем самой функции. - person fefe; 15.12.2011
comment
Нет, ваш пример не имеет отношения к исходной проблеме. Он имеет дело с вызовом функции, зависящей от аргумента шаблона, которая имеет другие правила поиска. Вы можете поменять местами func () и foo () и получить тот же результат. - person Gene Bushuyev; 15.12.2011