我有以下代码:
struct Iface
{
virtual int Read() = 0;
int Read(int x) {
return Read() + x;
}
};
template <typename Impl>
struct Crtp : public Iface
{
virtual int Read() {
return static_cast<Impl&>(*this).ReadImpl();
}
//using Iface::Read;
};
struct IfaceImpl : public Crtp<IfaceImpl>
{
int ReadImpl() {
return 42;
}
};
int main()
{
IfaceImpl impl;
impl.Read(24); // compilation error
Iface& iface = impl;
iface.Read(24); // always compiles successfully
}
msvc,gcc和clang都拒绝此代码,他们找不到方法Read(int x)
但是,如果我在using Iface::Read
中取消注释Crtp
,我的代码就会成功编译。
请注意,如果我参考Iface,我可以致电Read(int x)
为什么会这样?
答案 0 :(得分:7)
为什么会这样?
您的问题与CRTP无关。这是在正常继承方案中可能发生的名称隐藏问题。
当您致电impl.Read(24);
时,在Read
的班级范围内找不到成员函数名IfaceImpl
。然后将检查基类Crtp
的范围,并在那里找到名称Read
。然后名称查找停止,因此进一步的基类int Read(int x)
中的Iface
将不会被考虑用于重载解析,即使它在这里更合适。
通过using Iface::Read;
,您将名称Read
引入Crtp
的班级范围。然后可以通过正确的重载分辨率找到并选择它。
如果你通过Iface
引用来调用它,名称查找将会很好用。
或者您可以impl.Iface::Read(24);
明确地(并且丑陋地)调用它。
...名称查找检查范围如下所述,直到它找到至少一个任何类型的声明,此时查找停止,不再检查其他范围。