ARTICLE AD BOX
In a nutshell, name lookup looks for the InnerClass first in the scope of OtherClass. Next it inspects the base classes. In tn::InnerClass<OtherClass> the injected classname is just InnerClass. Namelookup stops there. The global namespace scope is not considered.
A simpler example:
template<typename T> struct base {}; struct derived : base<int> { using T = base; };You have a confluence of a few peculiarities of the language. These peculiarities are covered by other questions, but this particular combination might warrant some explanation (rather than closing this question as a duplicate).
The line
friend class InnerClass;makes use of the injected class name of the base class; see Does inheriting from a class bring it into the namespace?. It also makes use of the fact that the injected class name does not require template arguments; Why do I need to repeat template arguments of my base class in member initalizer list? is related. Thus, inside OtherClass, the identifier InnerClass is an alias for the base class tn::InnerClass<OtherClass>.
The line
friend class tn::InnerClass;fails because this is not how one declares a template to be a friend; see How do you mark a struct template as friend?. (Qualifying a name with tn:: instructs the compiler to look in the tn namespace, hence tn::InnerClass names your template. The injected class name requires additional qualification if it is to be found via the namespace: tn::InnerClass<OtherClass>::InnerClass.)
The line (apparently not tried before writing the question)
friend class tn::InnerClass<OtherClass>;works because this accounts for both the namespace and the template arguments. The question is incorrect in asserting "in this case tn:: must NOT be provided". Rather, tn:: MAY be provided as long as the result is the name of a specific class (not the name of a template).
18.8k5 gold badges20 silver badges44 bronze badges
Explore related questions
See similar questions with these tags.
