5

I was porting some code from MSVC(without permissive-) to linux and I learned that if you call constructor of a template base class in initializer list of your class you must specify all the template parameters or you get an error. Seems kind of redundant, since if you make a mistake in retyping the template parameters it is a hard error:

error: type 'Base<int, true>' is not a direct or virtual base of 'Derived'

full code here:

template <typename T, bool has_x>
struct Base
{
    Base(T t): t_(t){
    }
    T t_=0;
};



template <typename T>
class Derived : public Base<T, false>
{
public:
    // : Base<T, true> is hard error
    Derived(const T& t) : Base<T, false>(t) {}
};

int main()
{
    Derived d(47);
}

Is there a strong reason for this, or just standardization process never took time to special case this use case?

cigien
  • 57,834
  • 11
  • 73
  • 112
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • What is the code that produces the error? – Daniel Jun 24 '20 at 13:47
  • 3
    Pretty much nothing in the standard is special-cased on having a single base class. `Base` isn't a class; it's a template, so to name the class you're interested in, you need template parameters. And you could be inheriting from two or more `Base` specializations. – Nicol Bolas Jun 24 '20 at 13:48
  • 2
    Dupe of this but it has no answer: https://stackoverflow.com/questions/52632748/why-must-we-specify-template-arguments-for-template-base-class-when-calling-its?noredirect=1&lq=1 If one or the other gets one we can close the one that doesn't as a dupe. – NathanOliver Jun 24 '20 at 13:50
  • @Dani updated code with comment – NoSenseEtAl Jun 24 '20 at 13:50
  • @NicolBolas I could be, but if I do not Base I am talking about is not ambiguous. But I get your point regarding not bothering with special casing for this. – NoSenseEtAl Jun 24 '20 at 13:52

1 Answers1

11

You only need to do that when Derived is a template and the type of the base depends on its template parameters.

This compiles, for example:

template <typename T>
class Derived : public Base<int, false>
{
public:
    Derived(const T& t) : Base(t) {}
};

As far as I know, here (in member initializer list) Base is actually the injected-class-name of Base<...>, inherited from it like everything else.

And if the type of the base does depend on the template parameters, its inherited injected-class-name becomes inaccessible (at least directly), just like any other member inherited from it.

For a member variable/function, you'd add this-> to access it, but for a type member you need Derived:::

template <typename T>
class Derived : public Base<T, false>
{
public:
    Derived(const T& t) : Derived::Base(t) {}
};
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207