С++ 03 и С++ 11 имеют в первом абзаце [temp.friend]:
[Отредактированная цитата. Первая попытка пропустила вторую разницу в формулировках.]
Для объявления функции друга, которое не является объявлением шаблона:
если имя друга является квалифицированным или неквалифицированным идентификатором шаблона, объявление друга относится к специализации шаблона функции, иначе
если имя друга является квалифицированным идентификатором, и соответствующая функция nontemplate находится в указанном классе или пространстве имен, объявление друга относится к этой функции, в противном случае
[С++ 03:], если имя друга является квалифицированным идентификатором, и соответствующая специализация шаблона функции находится в указанном классе или пространстве имен, объявление друга относится к этой специализации шаблона, в противном случае
[С++ 11:], если имя друга является квалифицированным идентификатором, а соответствующий шаблон функции найден в указанном классе или пространстве имен, объявление друга относится к выведенной специализации этого шаблона функции, иначе,
имя должно быть неквалифицированным идентификатором, который объявляет (или переодетым) обычную (неэмплированную) функцию.
[Изменение в формулировке выглядит как пояснение для меня. Хотя я предполагаю, что могут быть разные способы интерпретации формулировки С++ 03 о "нахождении специализации в классе или пространстве имен".]
Мне интересна эта третья пуля. Я написал этот код, чтобы попытаться соответствовать его требованиям, но оба g++ 4.8.1 и clang++ 3.4 отклоняют код, будь то с -std = С++ 03 или -std = С++ 11:
template <class T> class R;
namespace N {
template <class T> void test(const R<T>&);
}
template <class T>
class R {
friend void N::test(const R<T>&); // 8
int m;
};
template <class T>
void N::test(const R<T>& rec) { rec.m; }
int main() {
R<int> r;
N::test(r);
}
Конечно, если я изменю строку 8 на
friend void N::test<>(const R<T>&);
применяется первая пуля, и программа принимается. g++ печатает полезное предупреждение, говоря, что друг "объявляет функцию без шаблона" и предлагает мне сделать именно это. Код, вероятно, получит больше точек стиля для ясности и безопасности.
Но не должен ли описанный выше код третьей крышкой и действителен? Объявление друга не является объявлением шаблона и использует идентификатор, который не является идентификатором шаблона в качестве имени. И нет никакой декларации функции nontemplate для второго пуля.
Это просто ошибка компилятора, общая для обоих? Или я что-то неправильно понял, и если да, то есть ли пример программы, демонстрирующей эту третью пулю?