在我的一个项目中遇到了以下情况,其中一个基类具有一个函数模板,该模板被派生类模板中的非模板函数隐藏。在类层次结构的更深处,非模板函数通过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>;
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>
MSVC和ICC毫无保留地接受此代码。
我想知道为什么在 有公共重载Base::get<T>
的情况下,编译器会抱怨Derived<T>::get
?
从using Base::get
中删除专用Derived<T>
会导致警告,提示您从基类中隐藏函数。因此,不幸的是,这也不是理想的选择。
在没有using Derived<T>::get
的情况下,必须在get
内限定对MoreDerived
的调用,因为否则它将不是从属名称。
任何想法,我在这里做错了什么?
答案 0 :(得分:4)
我相信这里适用的是[namespace.udecl]/17:
在未命名构造函数的 using-declarator 中,应可以访问引入的声明集中的所有成员。在 using-declarator < / em>命名构造函数,不执行访问检查。特别是,如果派生类使用 using-declarator 访问基类的成员,则该成员名称应可访问。 如果名称是重载成员函数的名称,则所有命名的函数均应可访问。 […]
(强调我的)与[namespace.udecl]/19组合:
由 using-declaration 创建的同义词通常具有 member-declaration 的可访问性。 […]
MoreDerived
中的using声明创建Derived::get
的同义词,其本身是由成员函数Derived::get
和成员函数模板Base::get
组成的重载集的同义词。后者在MoreDerived
中的using声明处无法访问(因为它在Derived
中是私有的)。因此,GCC和Clang是正确的,不应编译此代码。将Derived
中的using声明从私有部分移到公共部分,例如
template<typename T> class Derived : public Base
{
public:
using Base::get;
const T& get() const;
};
解决了问题...
答案 1 :(得分:0)
Michael Kenzel已经很好地解释了why your code did fail。
[...]但使get不能为Derived编译实际上是原始代码的功能
尽管我不鼓励使用这种模式,但是由于您违反了“是”关系,因此以下方法可能会帮您解决问题:
class Base
{
public:
template<typename T>
const T& get() const;
};
template<typename T> class Derived : public Base
{
public:
template<typename U>
U const& get() const = delete;
T const& get() const { return Base::get<T>(); }
};
可能更好的选择是简单地使模板getter受保护。
如果您认为可行,则Base
的私有继承也可以解决此问题;如果不是这样,另一种选择可能是将模板获取器移动到一个新的,独立的基类,然后该基类将被私有继承。
两个变体都可以防止
Derived<int> d;
static_cast<Base>(d).get<double>();
如果这仍然没有意义的话。