C++ inheritance and access of protected base class member: is doing it Java-style a bad idea?

StackOverflow https://stackoverflow.com/questions/23390053

  •  12-07-2023
  •  | 
  •  

Question

Unfortunately, I studied class design and design patterns mostly in the context of Java; thus, I sometimes have a hard time to translate a familiar pattern to C++.

Assume we want to have a base class which's functionality is extended by a sub-class. In Java, we would do something like this:

public class BaseClass<T> {
     //T is used here
     protected int foo = 0;         
}

public class DerivedClass<T> extends BaseClass<T> {
    public void incr_foo() {
         ++foo;
    }
}

Which I directly translated to C++:

template<class T>
class BaseClass {
protected:
    size_t foo = 0;
     //T is used here
};

template<class T>
class DerivedClass : public BaseClass<T> {
public:
     void incr_foo() {
          ++(this->foo);
     }
};

Since the C++ semantics of 'protected' diver from the Java-semantics, we need to use something like 'this->foo' to access protected members of the base class.

Edit: If we just use '++foo', the compiler gives the error: 'use of undeclared identifier foo'.

EditEnd

If you need to access several members of the base class, this can get a bit tedious and is not so nice to read.

Is this a good design desicion? If not, what better ways are there to achieve this?

Was it helpful?

Solution

This has nothing to do with the member being protected; you'd get the exact same error with a public member.

The real reason is that templates are involed. More specifically, that your class template has a base class which depends on a template parameter. This means that when parsing the template, the compiler will not (be able to) look into that base class to find inherited members which are used unqualified. It makes sense: when the template is parsed, the template parameter values are not known yet and thus the compiler has no idea what members the base class will have (remember partial and total specialisation exist!).

In order to overcome this, you must somehow tell the compiler that the name foo depends on template parameters (that it's a dependent name). Once you do so, the compiler will not try to resolve it when parsing the template; it will postpone resolution until the template is instantiated. At that point, template arguments are known and thus the base class can be checked.

You have three ways to mark a member name as dependent:

  1. Refer to the name through this, as you're doing: this->foo

  2. Refer to the name through base-class qualification: BaseClass<T>::foo

  3. Bring the name into the scope of the derived class:

    template<class T>
    class DerivedClass : public BaseClass<T> {
    protected:
         using BaseClass<T>::foo;  // from this point on, `foo` is a dependent name
    public:
         void incr_foo() {
              ++foo;  // works fine now
         }
    };
    

OTHER TIPS

BaseClass<T> is a dependent type - it depends on the template parameter used to instantiate DerivedClass. Until DerivedClass instantiated, the compiler doesn't know what the type is: it might be an instantiation of the generic template, or it might be an explicit specialisation, with different members.

So, within the definition of DerivedClass, there is no way to know which names refer to members of the base class. You have to specify that they are, using this-> or BaseClass::.

In a class with a non-dependent base class, you can use any accessible base-class members as if they were direct members, just as in Java.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top