为什么在尝试访问基本模板 class 的受保护成员时出现访问错误?

Why is there an access error when trying to access a protected member of a base template class?

常规 类:

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

但是BaseDerived类使用模板时报错:

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

这是为什么?

该错误大多与模板无关,并且在没有任何继承的情况下也会发生。简单的问题是表达式 &Base<T>::a 被解析为指向成员的指针,如以下代码片段所示:

#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();
}

输出为:

int * B::*
int * *

模板是错误表面的地方,因为我们被迫限定从属名称,因此无意中以指向成员的指针结构结束。

评论部分的两种解决方案都有效:您可以简单地写成&this->a,或者像我在这里所做的那样,将合格成员放在括号中。我不清楚为什么后者有效:operator::() 具有最高优先级,因此括号不会改变它。

正如人们所期望的那样,完全有可能在派生 class 中获取受保护基 class 成员的地址。据我所知,涉及模板时的错误消息是不正确且具有误导性的(但当我认为这是编译器的错误时,我通常是错误的...)。