Друг специализация шаблона без <>
С++ 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 для второго пуля.
Это просто ошибка компилятора, общая для обоих? Или я что-то неправильно понял, и если да, то есть ли пример программы, демонстрирующей эту третью пулю?
Ответы
Ответ 1
в строке // 8,
модифицированный код:
friend void N::test< R<T> >( R<T>&);
тоже.
friend void N::test<R<T>>(const R<T>&);//one type is friend with one type #1
friend void N::test<>(const R<T>&);// one type is friend with one type #2
Я использую некоторое доказательство кода, что # 1 равно # 2
Наконец, я пытаюсь ответить на ваш вопрос. Я не уверен, что это правильно.
friend void N::test(const R<T>&);
При создании экземпляра класса R R<T>
является известным типом. Однако функция
объявлен как функция друга и действительно не создает экземпляр шаблона функции, а затем
Функция friend - это функция, которая не существует. С точки зрения грамматики,
компилятор подскажет вам, что это функция, а не шаблон
N::test (r);
В этом месте функция создается, но компилятор не соответствует
друга перед объявлением в классе R, потому что вы не объявляете в качестве шаблона в R
вы просто объявляете функцию.
Ответ 2
Я думаю, что < > - условие вывода, так как из 14.8.2.6,
В объявлении, декларатор-id которого ссылается на специализацию шаблона функции, выполняется вывод аргумента шаблона для определения специализации, к которой относится объявление. В частности, это делается для явных инстанций (14.7.2), явных специализаций (14.7.3) и некоторых объявлений друзей (14.5.4).
В этом случае идентификатор декларатора не является специализацией, поэтому дедукция не выполняется.
Ответ 3
N:: test - это шаблонная функция, которая принимает класс под названием T
. Это класс не называется R<T>
. Измените функции соответствующим образом, и они будут работать.
namespace N
{
template <class T>
void test ( const T & );
}
template <class T>
class R
{
friend void N::test ( const R<T> & );
int m;
};
template <class T>
void N::test ( const T & rec ) { rec.m; }
int main ( )
{
R<int> r;
N::test ( r );
}