为什么公共重载与某些编译器上的private using指令冲突?

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::getDerived<T>导致大约从基类隐藏功能的警告。因此,不幸的是,这也不是理想的选择。

如果不使用using Derived<T>::get,则get必须在其中进行限定MoreDerived,否则它将不是从属名称。

有什么想法,我在这里做错了什么?

Mic*_*zel 4

我相信这里适用的是[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)

解决了问题...

  • @lubgr我想说,当你将 `Base::get` 变成一个普通的成员函数时,那么 `Derived::get` (这也是一个普通的成员函数)将隐藏它,因此,问题就消失了...... (2认同)