为什么 C++ 模板 class 可以访问其基类 class 的私有成员?

Why can a C++ template class access a private member of its base class?

使用 Visual Studio 2019 (v 16.7.3),我发现模板 class 可以访问其非模板基 class 的私有成员。这是预期的行为还是编译器错误?

当派生 class 不是模板 class 时,基础 class 的私有成员无法按预期访问:

class A
{
};

class Base
{
public:
    Base() : m_pA(new A()) {}
private:
    A* m_pA;
};

class Derived :
    public Base
{
public:
    Derived() : Base() {}
    A* get_a() { return m_pA; }  // 'Base::m_pA': cannot access private member declared in class 'Base' 
};

int main()
{
    Derived d;
}

== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==

但是,如果我将 Derived 设为模板 class,它会毫无怨言地编译:

template<class T>
class Derived :
    public Base
{
public:
    Derived() : Base() {}
    A* get_a() { return m_pA; }
};

int main()
{
    Derived<int> d;
}

== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==

贴出的代码没有调用Derived<int>::get_a(),所以模板成员函数没有被实例化(也没有私有父成员的“access”说话的).

添加以下 get_a() 调用会导致与非模板情况下相同的错误。

{
    Derived<int> d;
    d.get_a();  // error: 'A* Base::m_pA' is private within this context
}

[ EDIT ] 模板成员函数的隐式实例化(仅)使用时 在 [=23] 下的 C++ 标准中有规定=]:

Unless a member of a templated class is a declared specialization, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist or if the existence of the definition of the member affects the semantics of the program;

模板特化的显式定义(虽然不仅仅是声明)将导致所有成员的完整实例化,导致相同的错误。

extern template class Derived<int>; // explicit declaration - ok

template class Derived<int>;        // explicit definition - error: 'A* Base::m_pA' is private within this context

[ EDIT #2 ] 在评论(感谢 @Jarod42)中提出了给定代码属于“ill- formed, no diagnostic required”类别,这意味着允许编译器(虽然不是必需的)拒绝它,理由是 class 模板 Derived<T> 不可能(完整)实例化存在因为 get_a() 中的 private 成员访问,即使从未使用过成员函数

此解释基于 temp.res.general/6.1 的以下部分:

The program is ill-formed, no diagnostic required, if:

  • no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or [...]
  • 如果“valid specialization can be generated”表示整个模板的完整explicit特化class,则给定的代码片段确实是“格式错误的 NDR”。

  • 但是,如果“有效特化”被理解为涵盖模板 class 的隐式特化,(仅)具有必需的成员作为在前面引用的 temp.inst/4 中定义,则给定的代码片段不属于“ 格式错误的 NDR”,因为 get_a() 从未实例化(根据 temp.inst/11: "一个实现 不应隐式实例化 一个函数模板,一个变量模板,一个成员模板, 一个非虚成员函数成员 class 或模板化 class 的静态数据成员,或 constexpr if 语句的子语句, unless需要这样的实例化").

链接的demo表明gcc采用后一种解释,而clang采用前一种解释。

在任何一种情况下,答案都是如果模板没有因为“格式错误的 NDR”而被预先拒绝,因为 no-valid-specializations-can-exist 子句, 然后它将编译并正常工作 只要 get_a() 没有被实例化,无论是通过直接引用隐式地,还是通过模板的显式特化 class .