2

I'm experiencing a problem accessing a protected class member from a class template derived from another class template. I have three class templates, the second derived from the first, and and third one derived from the second. Specifically,

class1.h:

template <typename T> class class1
{
protected:
    T data;
    int a;
public:
    class1();
    void someMethod();
};

class2.h:

#include "class1.h"

template <typename T> class class2: public class1<T>
{
    using class1<T>::a;
    T otherData;
public:
    class2();
};

class3.h:

#include "class2.h"

template <typename T> class class3: public class2<T>
{
    using class2<T>::a;
public:
    class3();
};

class2.cpp:

#include "class2.h"
#include <iostream>

template <typename T> class2<T> :: class2()
{
    std::cout<<"Creating class2 object!"<<std::endl;
    a = 2;
}

template class class2<double>;

Finally, class3.cpp:

#include "class3.h"
#include <iostream>

template <typename T> class3<T> :: class3()
{
    std::cout<<"Creating class3 object!"<<std::endl;
    a = 3;
}

template class class3<double>;

When I compile class2.cpp into an object file, like this:

g++ -c -O3 -std=c++11 -Wall -o class2.o class2.cpp

everything goes well. However, a get an error when compiling class3.cpp in the same way. A following error pops up:

In file included from class2.h:4:0,
                 from class3.h:4,
                 from class3.cpp:1:
class3.h: In instantiation of ‘class class3<double>’:
class3.cpp:11:16:   required from here
class1.h:9:6: error: ‘int class1<double>::a’ is protected
  int a;
      ^

Replacing using class2<T>::a; with using class1<T>::a; in class3 doesn't help. What's causing this error exactly and how can I avoid it if I really need to access variable a from within class3? Why is it that the first level of inheritance (class2) doesn't detect any problems, and the second one (class3) does? Thanks for any comments.

NOTE: I tried the same type of inheritance, but without templates and removed both lines containing using, and compilation goes well (access to variable a is now granted in class3). The problem definitely has to do with templates.

MajinSaha
  • 188
  • 1
  • 9
  • 3
    You use private inheritance (it is the default `class` inheritance), so `a` becomes private field in the `class2` and it can't be accessed from the `class3`: https://stackoverflow.com/questions/860339/difference-between-private-public-and-protected-inheritance – Constructor Nov 20 '17 at 19:06
  • Did you try the same without templates first? – user0042 Nov 20 '17 at 19:10
  • @Constructor It's true I forgot to put keyword "public" when doing inheritance, but removing this typo doesn't fix the problem. The error stays the same. – MajinSaha Nov 20 '17 at 19:31
  • @user0042 I tried and it now works. But I need that working with templates. – MajinSaha Nov 20 '17 at 19:31
  • @MajinSaha Provide a [MCVE] reproducing the problems with the template headers (also show the include guards) please. Note that template definitions [need to be seen along with their declarations](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file). – user0042 Nov 20 '17 at 19:36
  • @user0042 This *is* an MCVE. You can add include guards if you want, but they are not needed to reproduce the problem and don’t remove the problem if they exist, since nobody is including the same file multiple times. – Daniel H Nov 20 '17 at 19:41
  • 1
    @MajinSaha You said you removed *both the templating and the `using` statements*. If you keep the `using`s but with non-templates, the issue still shows up; see [here](https://godbolt.org/g/HQizau). – Daniel H Nov 20 '17 at 19:42
  • @DanielH I'm seriously missing _include guards_ or at least a `#pragma once` in the example though. Also as mentioned, implementation of templates in separate translation units may lead to problems. – user0042 Nov 20 '17 at 19:45
  • @user0042 They are good style and very useful in preventing errors, but they are not a required part of a C++ language. You can’t say “provide a minimal example” and in the same comment ask for adding something which is not required for reproducing the problem. Well, demonstrably you *can*, but you shouldn’t. – Daniel H Nov 20 '17 at 19:47
  • @DanielH Well, that's why I left a comment about these things and not an answer, OK? – user0042 Nov 20 '17 at 19:49
  • @user0042 I had include guards originally, but they didn't serve any purpose for this tiny example (there is no overlapping of headers) so I intentionally removed them before posting here. Sorry, I didn't know they were required by StackOverflow standards. – MajinSaha Nov 20 '17 at 20:57

1 Answers1

2

Your class2 definition is the same as the following:

template <typename T> class class2: public class1<T>
{
private: // default section
    using class1<T>::a;
    T otherData;
public:
    class2();
};

It is because private is the default section for the members of classes. So the a member becomes private here and can't be inherited by the class3. You should explicitly place using class1<T>::a; statement in protected or public section of the class2 definition:

template <typename T> class class2: public class1<T>
{
    T otherData;
protected: // <-- added
    using class1<T>::a;
public:
    class2();
};

Now it can be accessed from the class2 derived classes (including class3).

Constructor
  • 7,273
  • 2
  • 24
  • 66
  • Damn! So that's what the problem was!! I've always thought the line containing keyword "using" was only used for some name visibility purposes and never participated in distributing access rights. Now I understand it actually matters whether you place it under private, protected or public. Thanks a lot! – MajinSaha Nov 20 '17 at 21:02
  • Why does it not work if `class3` explicitly says `using class1::a`, though? And either way, the error message could be better; it complains that `a` was declared `protected`, which shouldn’t be a problem. – Daniel H Nov 20 '17 at 21:11
  • @DanielH `a` becomes private in `class2` (due to the placement of the `using` statement in the `private` section) and can't be accessed from `class3`. `using` statement in the `class3` can't affect this. – Constructor Nov 20 '17 at 21:41
  • 1
    @DanielH The same effect could be achieved if the `using` statement would be removed from the `class2` definition. But due to the templates it is used to make `a` be accessible in the `class2` methods without full qualification (via `this->` or `class1::`). – Constructor Nov 20 '17 at 21:47