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

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

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

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 设为模板类,它会毫无怨言地编译:

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()因此模板成员函数不会被实例化(并且没有私有父成员的“访问”可言)。

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

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

[编辑] 模板成员函数的隐式实例化(仅)在使用时在 C++ 标准中规定在temp.inst/4 下:

除非模板化类的成员是已声明的特 化, 否则当在需要成员定义存在的上下文中引用特化时,或者如果成员定义的存在影响了成员的语义,则该成员的特化将被隐式实例化。程序;

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

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

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

[编辑 #2 ] 在评论中提出的论点(感谢@Jarod42)认为给定的代码属于“格式错误,不需要诊断”类别,这意味着允许编译器(尽管不是必需的)基于理由拒绝它即使从未使用过成员函数,也不会Derived<T>因为private成员访问而存在类模板的可能(完整)实例化。get_a()

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

程序 格式错误,无需诊断如果:

  • 不能 为模板或 constexpr if 语句的子语句生成 有效的特化,如果模板中的语句未实例化,或者 [...]
  • 如果“可以生成有效的专业化”被认为是整个模板类的完整显式专业化,那么给定的代码片段确实是“格式错误的 NDR ”。

  • 但是,如果“有效专业化”被理解为覆盖模板类的隐式专业化,其中(仅)前面引用的temp.inst/4 中定义的必需成员,则给定的代码片段不属于“格式错误的NDR” “因为get_a()永远不会被实例化(根据temp.inst/11:”一个实现 不应隐式实例化 一个函数模板、一个变量模板、一个成员模板、 一个非虚拟成员函数一个成员类或模板化类的静态数据成员,或 constexpr if 语句的子语句, 除非需要这样的实例化")。

链接的演示显示gcc采用后一种解释,而clang前者。

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


以上是为什么C++模板类可以访问其基类的私有成员?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>