安全地删除C ++隐藏的虚拟警告

时间:2013-01-11 11:01:18

标签: c++ virtual static-assert

我从Sun C ++ 5.10编译器那里得到一个关于我正在改变的一些现有代码中的隐藏虚拟方法的编译警告。无论出于何种原因,作者都没有为给定的数据类型实现函数的覆盖。我在这里重新创造了这种情况:

// First the data types
struct Shape {};
struct Square : public Shape {};
struct Circle : public Shape {};
struct Triangle : public Shape {};

// Now the visitor classes
struct Virtual
{   
    virtual ~Virtual() {}

    virtual void visit( Square& obj ) {}
    virtual void visit( Circle& obj ) {}
    virtual void visit( Triangle& obj ) {}
};

struct Concrete : public Virtual
{   
    void visit( Square& obj ) {}
    void visit( Circle& obj ) {}
};

int main()
{   
    Concrete myConcrete;

    return 0;
}

Concrete类未实现void visit( Triangle& obj ) {},这导致以下错误消息:

"pv_block.cpp", line 20: Warning: Concrete::visit hides the virtual function
Virtual::visit(Triangle&).

代码工作正常,但删除此警告消息会很好。因此,我希望实现该函数,以便编译器满足,但不能使用 - 最好在编译时检测 - 因为目前显然没有必要。

有没有办法实现编译断言以允许编译但阻止使用?我无法访问Boost或C ++ 11。

4 个答案:

答案 0 :(得分:5)

为什么要阻止使用?即使您(以某种方式)确实阻止它,以便以下(A)无法编译:

Triangle t;
Concrete x;
x.visit(t);

以下(B)仍然有效:

Triangle t;
Concrete x;
static_cast<Virtual&>(x).visit(t);

所以IMO,试图阻止它被调用是没有意义的。我会通过在Concrete类中添加使用声明来解决警告,如下所示:

struct Concrete : public Virtual
{ 
    using Virtual::visit;
    void visit( Square& obj ) {}
    void visit( Circle& obj ) {}
};

这将使警告静音,并启用(A)。但我不认为在这种情况下启用(A)是错误的。

答案 1 :(得分:1)

虽然我不一定同意你的设计;我认为警告是有效的,应该注意作为重新考虑的例子。但是,如果这是您想要做的,您可以将声明放在那里并更改对受保护的访问权限。

struct X {
   virtual void f() {}
   virtual void f(int) {}
};

struct Y : public X {
   virtual void f() {}
   virtual void f(int);
};

int
main() {
   Y y;
   y.f(10);
}

undefined reference to `Y::f(int)'

答案 2 :(得分:1)

唯一真正的解决方案是禁用编译器警告。 据推测,基类提供了一个默认实现,和 具体课程很满意。否则,你可以提供 转发到默认实现的实现 在基类中:

void Concrete::visit( Triangle& obj ) { Virtual::visit( obj ); }

就个人而言,我发现这是一种过度的措辞,而且宁愿 躲开它。

答案 3 :(得分:1)

  

因此,我希望实现该功能,以便编译器满足,但不能使用 - 最好在编译时检测 - 因为目前显然没有必要。

执行此操作的一种方法是将Concrete::visit(Triangle&)声明为私有:

struct Concrete : public Virtual
{   
   void visit( Square& obj ) {}
   void visit( Circle& obj ) {}
private:
   void visit (Triangle& obj);
};

请注意缺少实施。这消除了编译器警告,但这样做的代价是将当前由myConcrete.visit(myTriangle)导致的编译时错误更改为链接时错误。

即使使用此声明,通过将Concrete对象强制转换为父类,仍然可以拥有visit对象Triangle Concrete对象:{{1} }。这是一个问题,也是一个非常大的问题。目前的代码违反了Liskov替代原则。


使用Angew提出的static_cast<Virtual&>(myConcrete).visit (myTriangle)解决方案可能更好。现在,班级设计更接近服从Liskov替代。请注意,关于using(和static_cast<Virtual&>(myConcrete).visit (mySquare))的Liskov替换仍然存在问题。