1

最小的例子:

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的评论行保持评论):

  • 为什么编译器会抱怨它visitor3::visit(B&)正在隐藏visitor2::visit(C&)
  • 为什么它抱怨visitor3::visit(B&)隐藏visitor1::visit(A&)(这里有什么不同)?

附加问题:我如何实现这两者visitor2::visit(C&)visitor1::visit(A&)不是隐​​藏visitor3using visitor2::visit;够用吗visitor3

4

2 回答 2

1

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

再加上重载只发生在同一范围内定义的函数之间的规则。一旦编译器找到该名称,同一范围内的所有定义都会参与重载。编译器不会在外部范围内查找可能与名称匹配的内容。那就是疯狂。

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

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

于 2018-11-24T13:21:27.780 回答
1

名为 的成员(它可能是数据成员)的声明void visit(B&)或任何声明隐藏了名为 的父级的任何成员。visitint visit;visitor3visit

这里编译器的警告更多的是好东西。它发现了一个常见的错误模式,即在不考虑重载的情况下覆盖基本虚拟成员函数。但它不会检测到所有隐藏的名称,或者不会对所有这些名称发出警告。无论如何,一切都visit来自visitor1并被visitor2隐藏。

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

(通过 using 声明声明的这些成员的行为非常接近,就好像它们是在该类中首次声明的实际成员一样。即使重载决议也认为它们的隐含对象参数属于派生类的类型。)

于 2018-11-24T12:42:55.727 回答