具有两个以上类的“隐藏的重载虚拟函数”

时间:2018-11-24 12:13:32

标签: c++ overloading name-hiding

最小示例:

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&)都没有隐藏在visitor3中? using visitor2::visit;中的visitor3是否足够?

2 个答案:

答案 0 :(得分:1)

void visit(B&)中的visit声明或任何名为int visit;的成员(可能是数据成员visitor3)的声明都隐藏了名为{ {1}}。

在这里,编译器的警告更多的是好东西。它认识到一个常见的错误模式,该模式包括覆盖基本的虚拟成员函数而无需照顾重载。但是它不会检测到所有隐藏的名称,也不会对所有隐藏的名称发出警告。无论如何,visitvisit中的所有visitor1都是隐藏的。

其他答案:名称基类成员的使用声明是基类中所有具有该名称且可见的成员的派生类中的声明。因此,这实际上包括那些在基类中也由using声明声明的成员。

(通过using声明声明的这些成员的行为就像他们是该类中首先声明的实际成员一样。甚至重载解析也将其隐含对象参数视为派生类的类型。)

答案 1 :(得分:1)

“隐藏”是实际情况的简写。规则很简单():查找名称时,编译器在当前作用域中启动;如果找到该名称,就完成了。如果找不到,它将移至下一个封闭范围。重复直到完成。

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

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

因此,void visit(B&)中的visitor3在所有基类中都隐藏了visit所有其他定义。名称是在visitor3中定义的,因此编译器不会在其他地方显示。