From: DeMarcus on
On 2010-05-14 01:39, Alf P. Steinbach wrote:
> 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.
>

Yes, I want to call the "closest" fnc. I solved it by doing following.

struct B : A, B_helper
{
using B_helper::fnc;
};

I guess that is the proper way to do it?

It would be nice to bring in all functions from B_helper with something like

using B_helper;

But that's not possible, right?





--
[ 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 14, 8:49 am, "Alf P. Steinbach" <al...(a)start.no> wrote:
> * 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.

Suppose we use a broad definition of "covariant" to describe a
subtyping relationship ala
http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29

With that, I'm sorry. What exactly are you trying to get at? This is
how I understand it:

First example:
1) The (static) type of the expression "B()" is "B".
2) Compile-time lookup of the name "foo" as a member of B finds
B::foo.
3) The (static) type of the expression "B().foo()" thus has the type
of the return type of B::foo, aka "B&".
4) Compile-time lookup of the name "b" as a member of B finds B::b.
5) The (static) type of the expression "B().foo().b()" thus has the
type of the return type of B::b, aka "B&".
6) Compile-time lookup of the name "a" as a member of B finds A::a.
Thus it is well formed.

Second example:
1) The (static) type of the expression "B()" is "B".
2) Compile-time lookup of the name "a" as a member of B finds A::a.
3) The (static) type of the expression "B().a()" thus has the type of
the return type of A::a, aka "A&".
4) Compile-time lookup of the name "foo" as a member of A finds
A::foo.
5) The (static) type of the expression "B().a().foo()" thus has the
type of the return type of A::foo, aka "A&".
6) Compile-time lookup of the name "b" as a member of A fails. The
compiler finds that A has no such member.
Thus it requires a diagnostic.

How does "covariant" come into this analysis at all? A::foo could just
as easily have the return type "int" or "std::string", and the
analysis would come out the same.

PS: Yes, overloading, overriding, and hiding are distinct. I'm not
sure if the C++ standard explicitly mentions all of them. I assume it
has to, by name or implicitly.


--
[ 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
* Alf P. Steinbach:
> * 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.

Sorry for the 'const's (they shouldn't be there). I have an old computer that takes a nap now and then when MS Security Essentials or in general MS Update, and lately it seems also Thunderbird 3.x garbage collection, thinks it's a good idea. Editing text with this beastie is difficult to say the least, but, sorry.

Cheers,

- 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
On 15.05.2010 00:22, * Joshua Maurice:
> On May 14, 8:49 am, "Alf P. Steinbach"<al...(a)start.no> wrote:
>> * 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.
>
> Suppose we use a broad definition of "covariant" to describe a
> subtyping relationship

Huh. Please be more precise.


> ala
> http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29

That's a particularly bad reference. They define "convariant" [sic!] as "converting from narrower (float) to wider (double)". That's just rubbish.

To understand "covariant" focus on the word parts, "co" and "variant".

"co" means, in this context, "in the same way", and "variant" refers to a varying in specificity.

A type T /varies/ in specificity, and it varies /with/ or like something X.

Without implicit or explicit reference to the comparision referent X the term covariant is utterly meaningless, since covariant is a relative thing.

T, in the previous paragraph, is covariant with X because they vary in the same way. For example, T might be a method result type of a method foo, and X might be a choice of a class in a chain of derived classes. As you move the choice of X up or down,
the X-covariant T varies in specificity in the same way as X.


> With that, I'm sorry.

Me too.

I hope someone takes the time to fix up that silly-page that's misleading you.


> What exactly are you trying to get at? This is
> how I understand it:
>
> First example:
> 1) The (static) type of the expression "B()" is "B".
> 2) Compile-time lookup of the name "foo" as a member of B finds
> B::foo.
> 3) The (static) type of the expression "B().foo()" thus has the type
> of the return type of B::foo, aka "B&".
> 4) Compile-time lookup of the name "b" as a member of B finds B::b.
> 5) The (static) type of the expression "B().foo().b()" thus has the
> type of the return type of B::b, aka "B&".
> 6) Compile-time lookup of the name "a" as a member of B finds A::a.
> Thus it is well formed.
>
> Second example:
> 1) The (static) type of the expression "B()" is "B".
> 2) Compile-time lookup of the name "a" as a member of B finds A::a.
> 3) The (static) type of the expression "B().a()" thus has the type of
> the return type of A::a, aka "A&".
> 4) Compile-time lookup of the name "foo" as a member of A finds
> A::foo.
> 5) The (static) type of the expression "B().a().foo()" thus has the
> type of the return type of A::foo, aka "A&".
> 6) Compile-time lookup of the name "b" as a member of A fails. The
> compiler finds that A has no such member.
> Thus it requires a diagnostic.

Ignoring my "const" typo, this is a correct analysis, yes.


> How does "covariant" come into this analysis at all?

Which you then answer, by way of a false assertion :-), in your next question, same paragraph:


> A::foo could just
> as easily have the return type "int" or "std::string", and the
> analysis would come out the same.

No, in that case none of the example expressions would compile.

The first example expression works because foo has a result type that is covariant with respect to each foo declaration's containing class.

For a type associated with a class member the containing class is so strongly implied as reference for "whatever-variant" that we can say just "covariant" (or "contravariant", or "invariant"), without mentioning the classes.


> PS: Yes, overloading, overriding, and hiding are distinct. I'm not
> sure if the C++ standard explicitly mentions all of them. I assume it
> has to, by name or implicitly.


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: Paul Bibbings on
"Alf P. Steinbach" <alfps(a)start.no> writes:

> * 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.

I'm with Joshua on this one. It does not, at all, make sense to talk
about covariant result types for non-virtuals.

[class.virtual] �10.3/2
"If a virtual member function vf is declared in a class Base and in a
class Derived, derived directy or indirectly from Base, a member
function vf with the same name and same parameter list as Base::vf is
declared, then Derived::vf is also virtual (whether or not it is so
declared) and it /overrides/ Base::vf." [sic, italics]

This *defines* `overriding'.
[class.virtual] �10.3/5
"The return type of an overriding function shall be either identical
to the return type of the overridden function or /covariant/ with the
classes of the functions. If function D::f overrides a function
B::f, the return types of the function are covariant if they satisfy
the following criteria:

- // ..." [sic, italics]

This *defines* `covariance' in terms of overriding functions *only*.

.... both of these according to:

[intro.defs] �1.3/2
"Terms that are used only in a small portion of this International
Standard are defined where they are used and italicized where they
are defined."

The term (indeed the /word/) `covariant' occurs nowhere else in the
Standard except in its two uses in �10.3/5, `covariance' occurring
nowhere.
> 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.

Your example above does *not* illustrate covariance, despite the surface
similarities.
> 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.

Whatever `covariant' means "in general" is irrelevant owing to the fact
that the ISO defines the term specifically according to what it
requires for its purposes.

> That said, as you note, covariance has nothing to do with the OP's question.
>
>
> Cheers & hth.,
>
> - Alf

{ Please avoid such a "dangling" quotation; you don't need to (and
should not) quote any part that you're not replying to. -mod }

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