3

我从 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

4 回答 4

5

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

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

以下(B)仍然有效:

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

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

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

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

于 2013-01-11T11:22:22.620 回答
1

唯一真正的解决方案是禁用编译器警告。大概基类提供了一个默认实现,具体类对此很满意。否则,您可以提供一个实现,该实现转发到基类中的默认实现:

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

就个人而言,我认为这是多余的措辞,并且宁愿避免它。

于 2013-01-11T11:18:14.200 回答
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成为对象: . 这是一个问题,而且是一个非常大的问题。目前的代码违反了 Liskov 替换原则。TriangleConcretestatic_cast<Virtual&>(myConcrete).visit (myTriangle)


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

于 2013-01-11T12:38:30.377 回答
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)'
于 2013-01-11T11:08:13.130 回答