Topic: Overloaded Virtuals


Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/02/26
Raw View
fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson) writes:

|>  shapcott@wallaby.cig.mot.com (David B. Shapcott [C]) writes:
|>
|>  >While writing code recently I encountered some unexpected compiler behaviour.
|>  >Consider (these examples vastly simplified):
|>  >
|>  >class Foo
|>  >{
|>  >public:
|>  >  virtual int apply();
|>  >  virtual int apply(int);
|>  >  virtual int apply(char *);
|>  >}
|>  >
|>  >class Bar:public Foo
|>  >{
|>  >public:
|>  >  int apply(int);
|>  >}

    [...]
|>  The solution is to use
|>
|>   class Bar: public Foo
|>   {
|>   public:
|>     int apply(int);
|>     int apply(void) { return Foo::apply(); }
|>     int apply(const char *s) { return Foo::apply(s); }
|>   }

Wouldn't

    class Bar : public Foo
    {
    public:
     using Foo::apply ;
     int apply( int ) ;
    } ;

do just as well?

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/02/27
Raw View
James Kanze <james-albert.kanze@vx.cit.alcatel.fr> writes:

 >fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson) writes:
 >
 >|>  The solution is to use
 >|>
 >|>   class Bar: public Foo
 >|>   {
 >|>   public:
 >|>     int apply(int);
 >|>     int apply(void) { return Foo::apply(); }
 >|>     int apply(const char *s) { return Foo::apply(s); }
 >|>   }
 >
 >Wouldn't
 >
 >    class Bar : public Foo
 >    {
 >    public:
 >     using Foo::apply ;
 >     int apply( int ) ;
 >    } ;
 >
 >do just as well?

Yes, it would, presuming your compiler supports `using'.
I should have said "a solution", not "the solution".

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: herbs@cntc.com (Herb Sutter)
Date: 1997/02/22
Raw View
fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson) wrote:
>The solution is to use
>
> class Bar: public Foo
> {
> public:
>   int apply(int);
>   int apply(void) { return Foo::apply(); }
>   int apply(const char *s) { return Foo::apply(s); }
> }

Or, as Fergus knows but which is not yet available in all compilers:

    class Bar : public Foo
    {
    public:
        using Foo::apply; // makes other inherited overloads visible
        int apply(int);   // override; MUST come after using declaration
    };

This is usually better, since going forward you'll also automatically get
any new overloads added to Foo.

Now another question... 7.3.3/1 says that a name specified in a using-decl
in a class or namespace scope shall not already be a member of that scope
(hence the "MUST" comment in my code above).  Does that make the following
program not well-formed?

    struct A     { int f();    };
    struct B : A { int f(int); };
    struct C : B { using A::f; using B::f; int f(double); };

IOW, C should have all f()'s visible, it adds a new C::f(), but B didn't
bring A::f() into its scope.  Now, as soon as I bring A::f into scope, I
cannot bring B::f into scope with another using-decl since the name "f" is
already in scope?  Both using-decls are required for this case to work as
desired, otherwise I must use forwarding functions as a workaround.

---
Herb Sutter (herbs@cntc.com)

Current Network Technologies Corp.
3100 Ridgeway, Suite 42, Mississauga ON Canada L5L 5M5
Tel 416-805-9088  Fax 905-608-2611
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: shapcott@wallaby.cig.mot.com (David B. Shapcott [C])
Date: 1997/02/24
Raw View
In article <5elru4$o1k@mulga.cs.mu.OZ.AU>,
Fergus Henderson <fjh@murlibobo.cs.mu.OZ.AU> wrote:
>
>The warning is correct.

But why?

>[...]
>However, my copy of gcc 2.7.2 accepts it, so I think perhaps
>you have oversimplified your example.

My fault.  IT around here has 17 versions of gcc installed on
different machines.  I must have compiled in the wrong xterm.  When I
made sure that the machine had 2.7.2 and my paths were set up
correctly, it compiled and dispatched the virtuals according to
overloading.

> [...]

So now 2.7.2 and MetroWerks dispatch the virtuals by the distinguished
names according to overloading and SparcWorks does not.  The questions
remains what justification exists for this behaviour in SparcWorks?
And if it is ANSI-compliant to hide Foo:apply(void) and
Foo::apply(char *) because of Bar::apply(int), why?

(And my 'vastly' simplified example did show the wrong pointer type in
main; only if the derived class ptr is used does the compiler issue
errors.)






--
D. Brad Shapcott [C] Contractor, Motorola Cellular Infrastructure Group

"Theory changes the reality it describes."
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/02/25
Raw View
shapcott@wallaby.cig.mot.com (David B. Shapcott [C]) writes:

>In article <5elru4$o1k@mulga.cs.mu.OZ.AU>,
>Fergus Henderson <fjh@murlibobo.cs.mu.OZ.AU> wrote:
>>
>>The warning is correct.
>
>But why?

Because name hiding works on names, not on functions;
or alternatively because overloading works only within
a single scope, and the member functions of a derived class
are in a different scope to the functions in the base class.

You can debate about whether or not this is a good thing, but
that is the way it works in C++.

Note however that the issue here is name hiding;
virtual function resolution is a different story.
The compilers were warning you that you couldn't access
all the overloaded base class functions via a derived class
pointer or reference.

>So now 2.7.2 and MetroWerks dispatch the virtuals by the distinguished
>names according to overloading and SparcWorks does not.  The questions
>remains what justification exists for this behaviour in SparcWorks?

If SparkWorks really does exhibit different behaviour than gcc 2.7.2
for the original example program you posted, then that is a bug in
SparkWorks, because the program is strictly conforming (there is no
unspecified or undefined behaviour), and gcc's behaviour is correct.

However, unless "SparcWorks" is a very old compiler, I think it is
unlikely that it would have this sort of bug; my guess is that
SparkWorks is probably gives the same behaviour as gcc on that example.

>And if it is ANSI-compliant to hide Foo:apply(void) and
>Foo::apply(char *) because of Bar::apply(int), why?

I suggest you have a look at the ARM (the C++ Annotated Reference
Manual, by Ellis and Stroustrup), pages 310-312, which has a discussion
of this issue.  The short answer is that ignoring scopes when resolving
overloading "would lead to surprises when an unsuspected function was
invoked by a call".

>(And my 'vastly' simplified example did show the wrong pointer type in
>main; only if the derived class ptr is used does the compiler issue
>errors.)

If the derived class pointer is used, then the program is ill-formed,
and a conforming compiler must issue a diagnostic.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: shapcott@wallaby.cig.mot.com (David B. Shapcott [C])
Date: 1997/02/21
Raw View
While writing code recently I encountered some unexpected compiler behaviour.
Consider (these examples vastly simplified):

class Foo
{
public:
  virtual int apply();
  virtual int apply(int);
  virtual int apply(char *);
}

class Bar:public Foo
{
public:
  int apply(int);
}


One compiler (SparcWorks) warned that Bar::apply(int) hid Foo::apply()
and Foo::apply(char *).  That compiler and another (gcc 2.7.2) would
reject something like:

Foo *foo = new Bar();

foo->apply();             // 1.
foo->apply("foobar");     // 2.

Saying that 1 had too few arguments and 2 the wrong kind.

Yet a third compiler (MetroWerks on Mac) happily compiled the code and
dispatched the overloaded virtuals according to my expectation.  I.e.

foo->apply();    // calls Foo::apply()
foo->apply(1);   // calls Bar::apply(int)
foo->apply("1"); // calls Foo::apply(char *)

Now, after reading the draft standard I can find no prohibition or constraint
on overloading virtual functions.  I rather suspect this is because the
compiler writers use the unmangled symbol in the vtable (which I would have
thought mondo incorrect).  I don't have a copy of the ARM, so I don't know if
this might be a pre-ANSI convention.

Before raising issues back with the people who make the compiler, I'd
like to ask if (a) this interpretation of ANSI C++ is correct
(overloaded virtuals allowed), and (b) was there EVER any convention
for using unmangled symbol names in vtables?  (I had to manually
mangle the function names so that the two compilers in question could
distinguish them, and that can't be right.)





--
D. Brad Shapcott [C] Contractor, Motorola Cellular Infrastructure Group

"Theory changes the reality it describes."
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/02/22
Raw View
shapcott@wallaby.cig.mot.com (David B. Shapcott [C]) writes:

>While writing code recently I encountered some unexpected compiler behaviour.
>Consider (these examples vastly simplified):
>
>class Foo
>{
>public:
>  virtual int apply();
>  virtual int apply(int);
>  virtual int apply(char *);
>}
>
>class Bar:public Foo
>{
>public:
>  int apply(int);
>}
>
>One compiler (SparcWorks) warned that Bar::apply(int) hid Foo::apply()
>and Foo::apply(char *).

The warning is correct.

>That compiler and another (gcc 2.7.2) would reject something like:
>
>Foo *foo = new Bar();
>
>foo->apply();             // 1.
>foo->apply("foobar");     // 2.
>
>Saying that 1 had too few arguments and 2 the wrong kind.

Rejecting code like that would not be right; apart from the fact
that your code uses the deprecated conversion from string literal to
`char *' (rather than using `const char *'), that code is fine.
However, my copy of gcc 2.7.2 accepts it, so I think perhaps
you have oversimplified your example.

If you wrote

 Bar *bar = new Bar();

 bar->apply();             // 1.
 bar->apply("foobar");     // 2.

then the compilers would be right to reject your code,
because Bar::apply(int) does hide Foo::apply(void) and Foo::apply(char *).

The solution is to use

 class Bar: public Foo
 {
 public:
   int apply(int);
   int apply(void) { return Foo::apply(); }
   int apply(const char *s) { return Foo::apply(s); }
 }

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]