Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing template base class's constructor from derived class

Tags:

c++

Please consider the following scenario:
1) A parameterized base class A<T>
2) A parameterized derived class B<T> deriving from A<T>.

When the derived class' ctor tries to reference the base class, a compilation error occurs:

/** Examines how to invoke the Base class ctor
    from the Derived class,
    when both classes are templates.
*/

#include <iostream>

using namespace std;


template<typename T>
class A                     /// base class
{
public:
    A(int i)
    {
    }
};


template<typename T>
class B : public A<T>         /// derived class
{
public:
    B(int i) :
        A {i}
    {
    }
};


int main()
{
    A<int> a {5};
    B<int> b {10};
}


Errors:

\main.cpp||In constructor 'B<T>::B(int)':|
\main.cpp|26|error: class 'B<T>' does not have any field named 'A'|

\main.cpp||In instantiation of 'B<T>::B(int) [with T = int]':|
\main.cpp|35|required from here|
\main.cpp|26|error: no matching function for call to 'A<int>::A()'|

\main.cpp|26|note: candidates are:|
\main.cpp|15|note: A<T>::A(int) [with T = int]|
\main.cpp|15|note:   candidate expects 1 argument, 0 provided|
\main.cpp|12|note: constexpr A<int>::A(const A<int>&)|
\main.cpp|12|note:   candidate expects 1 argument, 0 provided|
\main.cpp|12|note: constexpr A<int>::A(A<int>&&)|
\main.cpp|12|note:   candidate expects 1 argument, 0 provided|
||=== Build failed: 2 error(s), 2 warning(s) (0 minute(s), 0 second(s)) ===|


The compiler interprets the B class's ctor as initializing a field A in the B class, rather than as invoking the A class' ctor.

How can this be fixed?

like image 723
SSteven Avatar asked Apr 11 '26 00:04

SSteven


2 Answers

The name of the base class, when used in B<T>'s constructor, is a dependent name, because what it refers to depends on the template argument T. See https://en.cppreference.com/w/cpp/language/dependent_name.

The name lookup rules are different. Names are not available in the current scope (i.e. the constructor of B<T>), if they depend on template arguments of the current scope (T).

In that case the entire full name needs to be specified in B:

template<typename T>
class B : public A<T>         /// derived class
{
public:
    B(int i) :
        A<T> {i}
    {
    }
};

The same is true when accessing members of the base class in the derived class, for example it is necessary to do:

template<typename T>
class B : public A<T>
{
public:
    void f()
    {
        A<T>::g();  // defined in A
    }
};

In that case this->g() does also work, but it can have a different meaning.

It may be useful to define a typedef of the base class as a member of B<T>, for example:

class B : public A<T>         /// derived class
{
    using base = A<T>;
public:
    B(int i) :
        base {i}
    {
    }
};

Then the base class's template arguments need to be repeated only one time in the code.

like image 64
tmlen Avatar answered Apr 12 '26 14:04

tmlen


The injected class name will only work within that class not the derived class so change the code to following:

#include <iostream>

using namespace std;


template<typename T>
class A                     /// base class
{
public:
    A(int i)
    {
    }
};


template<typename T>
class B : public A<T>         /// derived class
{
public:
    B(int i) :
        A<T> {i} // added <T>
    {
    }
};


int main()
{
    A<int> a {5};
    B<int> b {10};
}
like image 35
Mohit Avatar answered Apr 12 '26 13:04

Mohit



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!