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

Why compiler does not see method of base class when using 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
}

msvc、gcc 和 clang 都拒绝此代码,它们找不到方法 Read(int x)

但是,如果我在 Crtp 中取消注释 using Iface::Read,我的代码将成功编译。

请注意,如果我引用 Iface,我可以调用 Read(int x)

为什么会这样?

Why does this happen?

您的问题与CRTP无关。这是一个在正常的继承场景中可能会发生的名称隐藏问题。

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

通过 using Iface::Read;,您将名称 Read 引入 Crtp 的 class 范围。然后可以通过重载解析正确找到和选择。

如果您通过 Iface 引用调用它,名称查找将正常工作。

或者您可以通过 impl.Iface::Read(24);.

显式地(丑陋地)调用它

Unqualified name lookup:

... name lookup examines the scopes as described below, until it finds at least one declaration of any kind, at which time the lookup stops and no further scopes are examined.