Question

I'm getting a compilation warning from Sun C++ 5.10 compiler about a hidden virtual method in some existing code that I'm changing. For whatever reason, the author has not implemented an override of the function for a given data type. I've recreated the situation here:

// 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;
}

The Concrete class does not implement void visit( Triangle& obj ) {} and this is causing the following error message:

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

The code works fine but it would be nice to remove this warning message. I'd like therefore to implement the function so that the compiler is satisfied but in such a way that it cannot be used - preferably detected at compile time - since it's clearly not necessary at present.

Is there a way to implement a compile assertion to allow compilation but prevent use? I don't have access to either Boost or C++11.

Was it helpful?

Solution

Why would you want to prevent use? Even if you (somehow) do prevent it so that the following (A) fails to compile:

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

the following (B) will still work:

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

So IMO, it doesn't make sense to try to prevent it from being called. I would solve the warning by adding a using declaration into the Concrete class, like this:

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

This will silence the warning, and enable (A). But I don't believe it's wrong to enable (A) in this case.

OTHER TIPS

While I don't necessarily agree with your design; I think the warning is valid and should be heeded as an example to reconsider. However, if this is what you want to do, you can just put the declaration in there and change the access to protected or something.

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)'

The only real solution is to disable the compiler warning. Presumably the base class provides a default implementation, and the concrete class is happy with it. Otherwise, you can provide an implementation which forwards to the default implemenatation in the base class:

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

Personally, I find this to be excess verbiage, and would rather avoid it.

I'd like therefore to implement the function so that the compiler is satisfied but in such a way that it cannot be used - preferably detected at compile time - since it's clearly not necessary at present.

One way to do this is to declare Concrete::visit(Triangle&) as private:

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

Note the lack of an implementation. This eliminates the compiler warning, but does so at the expense of changing the compile-time error that currently results from myConcrete.visit(myTriangle) into a link-time error.

Even with this declaration, it's still possible to have a Concrete object visit a Triangle object by casting the Concrete object to the parent class: static_cast<Virtual&>(myConcrete).visit (myTriangle). This is a problem, and a very big one. The code as it stands violates the Liskov substitution principle.


It's probably better to use the using solution proposed by Angew. Now the class design comes closer to obeying Liskov substitution. Note that there is still a problem with regard to Liskov substitution with regard to static_cast<Virtual&>(myConcrete).visit (mySquare) (and myCircle).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top