All examples in [temp.local] that use inheritance are using a templated Derived class, thus there is a need to access the Base with a qualified name, i.e. through the Derived, as in [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>
};
This is in order to overcome dependent name lookup rules.
There isn't an example for non-template Derived in this section of the specifications, but if the Derived is not templated, the following should work as well:
// same Base as above
struct Derived: public Base<int> {
Base* p; // meaning Derived::Base<int>
};
This is interpreted as:
struct Derived: public Base<int> {
Derived::Base* p;
};
Which is interpreted as:
struct Derived: public Base<int> {
Derived::Base<int>* p;
};
In our case:
class Logger : public Singleton<Logger> {
friend class Singleton;
};
Is same as:
class Logger : public Singleton<Logger> {
friend class Logger::Singleton;
};
Which is same as:
class Logger : public Singleton<Logger> {
friend class Logger::Singleton<Logger>;
};
It is to be noted that the definition in the spec of injected-class-name refers to:
The class-name is also bound in the scope of the class (template) itself; this is known as the injected-class-name.
I would take the fact that the word template
appears in parenthesis, as a hint by the spec that injected-class-name may appear also in a non-templated class. And in fact it is used elsewhere in the spec, in a non-template context, for example here and here.
To close this, the spec adds, at [dcl.type.simple]#note-1:
An injected-class-name is never interpreted as a template-name in contexts where class template argument deduction would be performed ([temp.local]).
So, I would say that your code is compliant with the specifications.
Note that [temp.local]#example-1 refers to cases where the compiler would not see class Y
as Y<int>
but rather as ::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
};
};
The last example works in our case as well, for declaring all types of Singleton befriended:
class Logger : public Singleton<Logger> {
template<class> friend class Singleton; // refers to ::Singleton
};
Above however doesn't compile in GCC due to an old reappearing bug in GCC.
To overcome the GCC bug, one can use the more verbose option:
class Logger : public Singleton<Logger> {
template<class> friend class ::Singleton; // OK with GCC and Clang
};
Code to play with: https://godbolt.org/z/Mcez17
friend class Singleton<Logger>;
to allow a specific instantiation of the template base class to be a friend, not all possible instantiations of the template. – Acrosstheboardtemplate <class L> friend class Singleton<L>;
- not what's in the OP. – Urbannatemplate <class> friend class Singleton;
), just a "friend class declaration". – Sianatemplate <class L> friend class Singleton<L>;
[temp.friend]/7: Friend declarations shall not declare partial specializations. – Obla