使用 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()
没有被实例化,它就会编译并正常工作,要么通过直接引用隐式,要么通过模板类的显式特化。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句