C++ Builder is wrong here. The reason why you should be able to use the ancestor's name in the constructor's member initializer list has to do with the concept of injected class name.
When you define a class, the compiler inserts the name of the class into that class and makes it refer to itself. This injected name is what allows you to use the name of the class without template arguments inside the class.
template <class T>
struct tt {
// The compiler behaves as if there was the following.
// typedef tt<T> tt;
};
This injected name is what gets looked up when you use the name tt
in the member initializer list.
(For the record, clang accepts the snippet without the template argument.)
Bonus: had you defined cc
as a class template with the template parameter T
and the ancestor was dependent on T
, the name tt
would not be found in the context of cc
's constructor's member initializer list.
template <class T>
class cc : tt<T> {
cc()
: tt<T>() /* You must use <T> here. */
{
}
};