"hidden overloaded virtual function" 超过 2 类

"hidden overloaded virtual function" with more than 2 classes

最小示例:

class A {};
class B : public virtual A {};
class C : public virtual B {};

// define two overloading virtual functions
// no inheritance, so no overriding/hiding in this class
struct visitor1
{
    virtual void visit(A& ) {}
    virtual void visit(B& ) {}
    virtual ~visitor1();
};

// covariant types are not allowed for overriding virtuals,
// so the C-visit function would hide A and B, so we add them
// using the "using" keyword
struct visitor2 : public visitor1
{
    using visitor1::visit;
    virtual void visit(C& ) {}
    virtual ~visitor2();
};

// the B-visit is a correct override
// without the use of "using" (or the C-visit) below, the
// compiler warns that the C-visit function from visitor3
// is being overridden
struct visitor3 final : public visitor2
{
    //using visitor2::visit;
    //void visit(C &) override {}
    void visit(B &) override {}
};

编译器:

$ clang++ -Wall -Wextra -Weverything -Wno-c++98-compat visitor.cpp 
visitor.cpp:32:14: warning: 'visitor3::visit' hides overloaded virtual function [-Woverloaded-virtual]
        void visit(B &) override {}
             ^
visitor.cpp:20:22: note: hidden overloaded virtual function 'visitor2::visit' declared here: type mismatch at 1st parameter ('C &' vs 'B &')
        virtual void visit(C& ) {}
                     ^

问题(假设 visitor3 的评论行保持评论状态):

附加问题:如何实现 visitor2::visit(C&)visitor1::visit(A&) 都不隐藏在 visitor3 中? visitor3 中的 using visitor2::visit; 是否足够?

void visit(B&) 的声明或任何名为 visit 的成员(它可能是数据成员 int visit;)在 visitor3 中的声明隐藏了父类的任何成员称为 visit.

这里编译器的warning更是个好东西。它识别出一个常见的错误模式,即在不考虑重载的情况下重写基本虚拟成员函数。但它不会检测所有隐藏名称,或者不会对所有隐藏名称发出警告。无论如何,visitor1visitor2 中的所有 visit 都被隐藏了。

附加答案:命名基class成员的using声明是基中所有成员的派生class中的声明class 具有该名称且可见。因此,这有效地包括了在基 class 中也由 using 声明声明的成员。

(这些由 using 声明声明的成员表现得非常接近,就好像它们是在 class 中首次声明的实际成员一样。即使重载决策也认为它们的隐含对象参数是派生的 class.)

"Hiding" 是 shorthand 的真实情况。规则很简单 ():查找名称时,编译器从当前作用域开始;如果它找到那个名字,它就完成了。如果找不到它,它会移动到下一个封闭范围。重复直到完成。

将其与重载发生的规则相结合在同一作用域定义的函数中。一旦编译器找到该名称,同一范围内的所有定义都会参与重载。编译器不会在外部作用域中查找可能与名称匹配的内容。那就是疯狂。

请注意,姓名查找仅查找姓名;它不依赖于找到的名称是否覆盖基 class 中的名称,也不依赖于当前作用域中是否存在多个具有相同名称的函数(即名称被重载)。找到名称后,搜索结束。该范围中该名称的所有定义都参与重载。

因此,visitor3 中的 void visit(B&) 隐藏了 所有基 class 中 visit 的所有其他定义 es。该名称在 visitor3 中定义,因此编译器不会在其他任何地方查找。