Все примеры в [temp.local], использующие наследование, используют шаблонный производный класс, поэтому необходимо получить доступ к Базе с квалифицированным именем, т.е. через производный, как в [temp.local]#example-2:
template <class T> struct Base {
Base* p;
};
template <class T> struct Derived: public Base<T> {
typename Derived::Base* p; // meaning Derived::Base<T>
};
Это делается для того, чтобы обойти правила поиска зависимых имен.
В этом разделе спецификаций нет примера для не шаблонного производного, но если производный не является шаблонным, следующее также должно работать:
// same Base as above
struct Derived: public Base<int> {
Base* p; // meaning Derived::Base<int>
};
Это интерпретируется как:
struct Derived: public Base<int> {
Derived::Base* p;
};
Что интерпретируется как:
struct Derived: public Base<int> {
Derived::Base<int>* p;
};
В нашем случае:
class Logger : public Singleton<Logger> {
friend class Singleton;
};
То же, что:
class Logger : public Singleton<Logger> {
friend class Logger::Singleton;
};
Что то же самое, что:
class Logger : public Singleton<Logger> {
friend class Logger::Singleton<Logger>;
};
Следует отметить, что определение в спецификации injected -имя-класса относится к:
Имя класса также привязано к области действия самого класса (шаблона); это известно как внедренное имя класса.
Я бы воспринял тот факт, что слово template
появляется в круглых скобках, как намек спецификации на то, что внедренное-имя-класса может появиться и в классе без шаблона. И на самом деле он используется в другом месте спецификации, в контексте, отличном от шаблона, например здесь и здесь< /а>.
Чтобы закрыть это, спецификация добавляет в [dcl.type.simple ]#примечание-1:
Введенное имя класса никогда не интерпретируется как имя шаблона в контекстах, где будет выполняться вывод аргумента шаблона класса ([temp.local]).
Итак, я бы сказал, что ваш код соответствует спецификациям.
Обратите внимание, что [temp.local]#example-1 относится к случаи, когда компилятор не увидит class Y
как Y<int>
, а скорее как ::Y
:
template<template<class> class T> class A { };
template<class T> class Y;
template<> class Y<int> {
Y* p; // meaning Y<int>
Y<char>* q; // meaning Y<char>
A<Y>* a; // meaning A<::Y>
class B {
template<class> friend class Y; // meaning ::Y
};
};
Последний пример работает и в нашем случае, для объявления всех типов Singleton дружественными:
class Logger : public Singleton<Logger> {
template<class> friend class Singleton; // refers to ::Singleton
};
Однако вышеприведенное не компилируется в GCC из-за появляющейся старой ошибки в GCC а>.
Чтобы преодолеть ошибку GCC, можно использовать более подробный вариант:
class Logger : public Singleton<Logger> {
template<class> friend class ::Singleton; // OK with GCC and Clang
};
Код для игры: https://godbolt.org/z/Mcez17
person
Amir Kirsh
schedule
25.01.2021
friend class Singleton<Logger>;
, чтобы позволить конкретному экземпляру базового класса шаблона быть другом, а не всем возможным экземплярам шаблона. - person Remy Lebeau   schedule 25.01.2021template <class L> friend class Singleton<L>;
, а не то, что в OP. - person StoryTeller - Unslander Monica   schedule 25.01.2021template <class> friend class Singleton;
), а просто в объявлении дружественного класса. - person aschepler   schedule 26.01.2021