From: DeMarcus on
Hi,

In �10.3/5 they explain covariant return types. Do they hold also for
non-virtual functions? I.e. is this legal on all compilers according to
the standard?

struct A_helper
{
A& fnc();
};

struct B_helper
{
B& fnc();
};

struct A : A_helper
{
};

struct B : A, B_helper
{
};

The inheritance structure would look like this.

A_helper
/
/
A
|
| B_helper
| /
| /
B

So B would have both following
A& fnc()
B& fnc()



Thanks,
Daniel




--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Chris Uzdavinis on
On May 11, 9:28 am, DeMarcus <use_my_alias_h...(a)hotmail.com> wrote:
> Hi,
>
> In �10.3/5 they explain covariant return types. Do they hold also for
> non-virtual functions? I.e. is this legal on all compilers according to
> the standard?

Covariant return type rules are irrelevant for nonvirtual functions. The
situation you're describing is only subject to name lookup issues, and in
particular, an ambiguity that arises when trying to call a function named
fnc() on an object of type B. However, once a function is resolved (which
would probably be through explicit qualification), then its validity
undergoes the same consideration as any other normal function call would.

Chris


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Joshua Maurice on
On May 12, 1:06 am, Chris Uzdavinis <cuz...(a)gmail.com> wrote:
> On May 11, 9:28 am, DeMarcus <use_my_alias_h...(a)hotmail.com> wrote:
>
> > Hi,
>
> > In �10.3/5 they explain covariant return types. Do they hold also for
> > non-virtual functions? I.e. is this legal on all compilers according to
> > the standard?
>
> Covariant return type rules are irrelevant for nonvirtual functions. The
> situation you're describing is only subject to name lookup issues, and in
> particular, an ambiguity that arises when trying to call a function named
> fnc() on an object of type B. However, once a function is resolved (which
> would probably be through explicit qualification), then its validity
> undergoes the same consideration as any other normal function call would.

Let me explain a bit more.

Let's first be clear on terminology. When a class has two different
functions with the same name (and thus they have different
signatures), the functions are said to "overload". When a derived
class implements a function with the same signature as a function in
one of its super types, that function is said to "override" the
super's function.

Let's talk about what virtual functions do. When you call a virtual
function, you are not calling a specific function at a specific
address. Instead, the following steps happen:

1- For the static type of the lvalue (which is probably a super class
of some other type for exposition), do overload resolution on the name
and arguments to pick the function signature. Note that return types
do not affect function overload resolution. Return types are not part
of function signatures. If it picks a function with a "bad" return
type, the program is ill formed and the compiler does not try to "pick
a better one".

2- Then, for the dynamic type of the object, aka most derived type,
and for all of its super types, find all of the functions which have a
matching signature. Again, return types are not part of a function's
signature. If there exists a "most derived" function, then that
function will be called. Otherwise the definition of the derived type
should have a compile error.

3- That function is called. It has a return value. The caller expects
the return value to be the return value of the function of the static
lvalue object type, aka of the super type. However, the actual
function called is (potentially) that of a derived type. A simple rule
would be to simply require that the return type of an overrided
function to be the same as the super's function. However, it makes
sense, for a virtual function in the super class of return type
pointer to T (or reference), to allow the actual function in the
derived class to return a pointer (or reference) to a derived type of
T. Ex:

class A {};
class derived_from_A : public A {};

class B { public: virtual A* foo(); };
class derived_from_B : public B { public: virtual derived_from_A*
foo(); };

When someone calls B::foo, he expects it to return a "A*", and
specifically a pointer to some A object. However, if it returns a "A*"
which actually points to a derived_from_A object, specifically the A
sub-object of a derived_from_A object, then caller should be able to
proceed.

That is covariant return types. It is allowed a function override to
have return type of a derived type of the return type of the function
in the super class. Without virtual, there is no overriding, only
overloading, and thus talking about covariant return types does not
make sense.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Alf P. Steinbach on
On 11.05.2010 16:28, * DeMarcus:
> Hi,
>
> In �10.3/5 they explain covariant return types. Do they hold also for
> non-virtual functions? I.e. is this legal on all compilers according to
> the standard?

As noted else-thread the issue of covariant return types is irrelevant to your question.


> struct A_helper
> {
> A& fnc();
> };
>
> struct B_helper
> {
> B& fnc();
> };
>
> struct A : A_helper
> {
> };
>
> struct B : A, B_helper
> {
> };
>
> The inheritance structure would look like this.
>
> A_helper
> /
> /
> A
> |
> | B_helper
> | /
> | /
> B
>
> So B would have both following
> A& fnc()
> B& fnc()

That's OK. Until you try to /call/ fnc on a B object. At that point two declarations of fnc will be found in different base classes, and the compiler doesn't even consider the result types or anything (e.g. it doesn't check whether one of methods are const), it just has a clear ambiguity at hand.


Cheers & hth,

- Alf

--
blog at <url: http://alfps.wordpress.com>


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Alf P. Steinbach on
* Joshua Maurice:
>
> Without virtual, there is no overriding, only
> overloading, and thus talking about covariant return types does not
> make sense.

There is overriding, overloading and hiding, although one may use the word
"overloading" to include the latter, and I suspect the standard does.

But anyway, with hiding in the picture it does make sense to talk about
covariant result types for non-virtuals.

E.g.,

struct A
{
A& a() { return *this; }
A& foo() const { return *this; }
};

struct B: A
{
B& b() { return *this; }
B& foo() const { return *this; } // Covariant result type
}

Now writing B().foo().b().a() should work nicely, but writing B().a().foo().b()
should not compile. The first expression works because the result type of foo is
covariant. The C++ limited support for virtual methods with covariant result
types is an orthogonal issue.

And invocation of that support is not what "covariant" means in general.

That said, as you note, covariance has nothing to do with the OP's question.


Cheers & hth.,

- Alf

--
blog at <url: http://alfps.wordpress.com>

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]