3

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?

1
  • 4
    You should use A<T> instead of A in the line B(int i): A{i} Commented Jul 4, 2018 at 11:20

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

1 Comment

The using base = A<T>; idiom is very useful in real world use cases where the template argument list is long and used more than once. It saves repetition and make code maintenance easier because if something changes, you have to change it only once (DRY principle).
1

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};
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.