为什么编译器在使用CRTP时看不到基类的方法

Ale*_*xey 5 c++ inheritance crtp

我有以下代码:

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
}
Run Code Online (Sandbox Code Playgroud)

msvc,gcc和clang都拒绝这个代码,他们找不到方法 Read(int x)

但是如果我using Iface::ReadCrtp我的代码中取消注释成功编译.

请注意,如果我参考Iface,我可以打电话 Read(int x)

为什么会这样?

son*_*yao 7

为什么会这样?

您的问题与CRTP无关.这是在正常继承方案中可能发生的名称隐藏问题.

调用时impl.Read(24);,Read无法在类的范围内找到成员函数名IfaceImpl.然后Crtp将检查基类的范围,并在Read那里找到名称.然后名称查找停止,因此int Read(int x)在进一步的基类中Iface不会考虑重载解析,即使它在这里更合适.

通过using Iface::Read;你将名称Read引入到类的范围内Crtp.然后可以通过正确的重载分辨率找到并选择它.

如果你通过Iface引用调用它,名称查找将很好.

或者你可以通过明确(和丑陋)调用它impl.Iface::Read(24);.

请参阅非限定名称查找:

... name lookup检查范围如下所述,直到它找到至少一个任何类型的声明,此时查找停止并且不再检查其他范围.