pah*_*pah 11 c++ inheritance using-directives
我在一个项目中遇到了以下情况,其中一个基类具有一个函数模板,该模板被派生类模板中的非模板函数隐藏。在类层次结构的更深处,非模板化函数通过using指令将函数显式带入范围。
这是一个简化的示例代码:
class Base
{
public:
template<typename T> const T& get() const;
};
template<typename T> class Derived : public Base
{
private:
using Base::get;
public:
const T& get() const;
};
template<typename T> class MoreDerived : public Derived<T>
{
public:
using Derived<T>::get; // <-- this causes the problem
const T& call_get() {
return get();
}
};
template class MoreDerived<int>;
Run Code Online (Sandbox Code Playgroud)
Godbolt:https://godbolt.org/z/5MQ0VL
上面的代码在GCC和Clang上失败,出现以下错误:
<source>:15:28: error: 'template<class T> const T& Base::get() const' is inaccessible within this context
15 | template<typename T> class MoreDerived : public Derived<T>
Run Code Online (Sandbox Code Playgroud)
MSVC和ICC接受此代码而不会提出投诉。
我想知道,为什么编译器抱怨Base::get<T>
虽然是一个公共超载Derived<T>::get
可用?
删除私人using Base::get
从Derived<T>
导致大约从基类隐藏功能的警告。因此,不幸的是,这也不是理想的选择。
如果不使用using Derived<T>::get
,则get
必须在其中进行限定MoreDerived
,否则它将不是从属名称。
有什么想法,我在这里做错了什么?
我相信这里适用的是[namespace.udecl]/17:
在未命名构造函数的using 声明符中,引入的声明集的所有成员都应可访问。在命名构造函数的using 声明符中,不执行访问检查。特别是,如果派生类使用using 声明符来访问基类的成员,则该成员名称应该是可访问的。如果名称是重载成员函数的名称,则所有指定的函数都应可访问。[…]
(强调我的)与[namespace.udecl]/19结合:
由using 声明创建的同义词具有member 声明的通常可访问性。[…]
using 声明MoreDerived
创建了一个同义词,Derived::get
该同义词本身就是由成员函数Derived::get
和成员函数模板组成的重载集的同义词Base::get
。后者在 using 声明处不可访问MoreDerived
(因为它在 中是私有的Derived
)。因此,GCC 和 Clang 是正确的,这段代码不应该编译。Derived
例如,将 using 声明从私有部分移至公共部分
template<typename T> class Derived : public Base
{
public:
using Base::get;
const T& get() const;
};
Run Code Online (Sandbox Code Playgroud)
解决了问题...