It works fine with regular classes:
class Base
{
public:
Base() {}
protected:
int* a;
};
class Derived : public Base
{
public:
Derived() {}
void foo() {
int** pa = &a;
}
};
int main() {
Derived* d = new Derived();
d->foo();
delete d;
}
But it reports an error when Base
and Derived
classes use templates:
‘int* Base<int>::a’ is protected within this context
template<typename T>
class Base
{
public:
Base() {}
protected:
int* a;
};
template<typename T>
class Derived : public Base<T>
{
public:
Derived() {}
void foo() {
int** pa = &Base<T>::a;
}
};
int main() {
Derived<int>* d = new Derived<int>();
d->foo();
delete d;
}
Why is that?
CodePudding user response:
The error is mostly unrelated to templates, and occurs also without any inheritance. The simple issue is that the expression &Base<T>::a
is parsed as a pointer to member, as the following snippet shows:
#include <iostream>
#include <typeinfo>
using namespace std;
class B
{
public:
void foo()
{
int* B::* pa = &B::a;
int** pi = &(B::a);
cout << typeid(pa).name() << endl;
cout << typeid(pi).name() << endl;
}
protected:
int* a;
};
struct D : public B
{
// Access to B::a is perfectly fine.
int* B::* pa = &B::a;
// But this causes a type error:
// "cannot convert from 'int *B::* ' to 'int **'
// int** pi = &B::a;
// Parentheses help to get the address of this->a ...
int** pi2 = &(B::a);
// ... and writing this->a helps, too ;-).
int **pi3 = &this->a;
// Of course, outside of templates we can simply write a!
int** pi4 = &a;
};
int main()
{
B b;
b.foo();
}
The output is:
int * B::*
int * *
Templates are where the error surfaces because we are forced to qualify dependent names and therefore unintentionally end up with a pointer-to-member construct.
Both solutions in the comment section work: You can simply write &this->a
or, as I did here, put the qualified member in parentheses. Why the latter works is not clear to me: operator::()
has the single highest precedence, so the parentheses do not change that.
It is, as one would expect, perfectly possible to take the address of a protected base class member in a derived class. The error message when templates are involved was, as far as I can see, incorrect and misleading (but then I'm usually wrong when I think it's the compiler's fault...).