Re: Partial overwriting of a method in subclasses
On 3 Mai, 04:45, walkman <mongoloid.manc...@googlemail.com> wrote:
class A {};
class B : public A {};
// service classes
class C
{
public:
virtual ~C() {};
virtual void func(A& guest) {}
virtual void func(B& guest) {}
};
class D : public C
{
public:
virtual ~D() {};
Not needed, because C already has a virtual dtor
(but doesn't harm of course).
virtual void visit(A& guest) { func(guest); }
virtual void func(B& guest) {}
};
[..]
Now, this code does not compile in GCC 4.1.2, nor does it compile in
the online Comeau C++ form (http://www.comeaucomputing.com/tryitout/),
where the first complains about a lack of definition of D::func(A&)
and the later about imposibility to cast down A& to B& so that it
could use D::func(B&) in D::visit(...), which is finally the same
problem.
Yes, it should not compile as currently written, v.i.
When I do not try to overwrite neither version of func(...),
everything is alright. Also when I overwrite both of them everything
is alright. Moreover Comeau C++ issues a warning that I only
"partially overwrote" the method func(...). I also noticed that the
fact that B is a subclass of A has nothing to do with the problem.
The last point is correct.
Questions:
1.- am I always forced to overwrite all versions of a method with the
same name, but with different signature?
No, you don't need to do so.
2.- why cannot compiler figure out itself that in D::visit(A&) the
call to C::func(A&) is the right solution? It seems to me natural as
this is how I always thought this kind of polymorphism works
(obviously wrong!)... Do I really have to explicitly implement it this
way?:
The compiler *could* figure out (or at least try it), but it is not
supposed to do so. The relevant point is the difference between
overloading and overwriting in C++. 10.2/4 explains the source
of your experience:
"If C contains a declaration of the name f, the declaration set
contains every declaration of f declared in C that satisfies the
requirements of the language construct in which the lookup
occurs [..]"
Para 5 explains why it "worked", when the derived class did
not introduce the name "func" into it's own scope:
"Otherwise (i.e., C does not contain a declaration of f or the
resulting declaration set is empty), S( f ,C) is initially empty.
If C has base classes, calculate the lookup set for f in each
direct base class subobject Bi, and merge each such lookup
set S( f ,Bi) in turn into S( f ,C)."
The reasons for these rules are better encapsulation of derived
classes and a more fine-grained control of it's base classes.
This point helps in the solution of your problem explained
in the very last point.
virtual void visit(A& guest) {C::func(guest)} - "C::" seems
superfluous...
In this single-class hierarchy situation the explicit qualification
is (a) unnecessary and (b) would change the meaning of the call.
The reason for (b) is, that calling C::func() you suppress a
virtual function call, thus it will always call the func overload from
C.
3.- does this something to do with the fact that C++ implements only
single-dispatch, instead of double-dispatch? (yet, when func is not
overwritten in D at all, compiler figure stuff out correctly!)
That point is not relevant for this discussion.
4.- how can I solve this problem without overwriting all the versions
of func(...)? In my real application C and D are visitors of an
extensive hierarchy and overloading all the versions of func(...)
would result into a big code bloat and explicit call to C::func() as
described in point 2 seems ugly to me.
Quite simple: Add a using-declaration for C::func to D:
class D : public C
{
public:
using C::func;
... // Remainder of class definition unchanged
};
According to 7.3.3/3
"[..] Such a using-declaration introduces the set of declarations
found by member name lookup (10.2, 3.4.3.1)."
and 7.3.3/16:
"For the purpose of overload resolution, the functions which are
introduced by a using-declaration into a derived class will be
treated as though they were members of the derived class. In
particular, the implicit this parameter shall be treated as if it
were a pointer to the derived class rather than to the base class.
This has no effect on the type of the function, and in all other
respects the function remains a member of the base class."
Greetings from Bremen,
Daniel Kr?gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]