This question relies on something called injected-class-name. From [class]
A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name.
For purposes of access checking, the injected-class-name is treated as if it were a public member name.
And from [temp.local]:
Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name. When it is used with a template-argument-list,
as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier of a friend class template declaration, it refers to the class template itself. Otherwise, it is equivalent
to the template-name followed by the template-parameters of the class template enclosed in <>
.
That is, within the definitions Bar1<T>
or Bar2<T>
, you can use Bar1
or Bar2
to refer to the full class type. That is, these declarations are equivalent:
void Foo(Bar2<T> const & other);
void Foo(Bar2 const & other);
However, the rules for lookup apply as normal. While there is an injected-class-name for Base
, that is a dependent name and so cannot be found via normal unqualified lookup. From [temp.dep]:
In the definition of a class or class template, the scope of a dependent base class (14.6.2.1) is not examined
during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.
which makes this:
Bar1() : Base() {}
ill-formed. Base
is unqualified lookup, and there is no such name Base
. There is a Base<T>::Base
(the injected-class-name there), but that scope is unexamined. You would have to either do qualified lookup:
Bar1() : Bar1<T>::Base() {}
Bar1() : Bar1::Base() { }
or not rely on the injected-class-name of Base
:
Bar1() : Base<T>() { }
VS is wrong in accepting Bar1
. Bar2
is perfectly OK, if more verbose than strictly possible. Nothing wrong with that.
Worth noting also that if the base weren't dependent, you could still use its injected-class-name even if it were a template:
template <class T> struct Base { };
struct Derived : Base<int> {
Derived() : Base() { } // OK, lookup finds Base<int>::Base
};