Covariant return types

M

Martin Stettner

Hi,

I would like to use covariant return types in mutual dependent classes like:

class IB;
class IA {
virtual IB* getIB() = 0;
};
class IB{
virtual IA* getIA() = 0;
};

class B;
class A : public IA{
B *getIB(); /// Here I get an error!
// If I replace with IB * getIB(); everything works fine
};

class B : public IB {
A* getIA();
};

I understand, that the compiler cannot know - at the time of the
declaration of A::getIB() - that B derives from IB. Is there a way to do
some sort of forward declaration with "inheritance information" in order
to get the example to work?

thanks in advance

Martin
 
M

Mike Wahler

Martin Stettner said:
Hi,

I would like to use covariant return types in mutual dependent classes like:

class IB;
class IA {
virtual IB* getIB() = 0;
};
class IB{
virtual IA* getIA() = 0;
};

class B;
class A : public IA{
B *getIB(); /// Here I get an error!
// If I replace with IB * getIB(); everything works fine
};

class B : public IB {
A* getIA();
};

I understand, that the compiler cannot know - at the time of the
declaration of A::getIB() - that B derives from IB.

That is not the problem. The problem is that you're trying
to override a virtual function, but your return value is not
the same as that of the function you're trying to override.
This is not allowed. All of: the name, arguments, and return
type must be identical.
Is there a way to do
some sort of forward declaration with "inheritance information" in order
to get the example to work?

What specifically are you trying to do?

-Mike
 
L

Larry Brasfield

Mike Wahler said:
That is not the problem. The problem is that you're trying
to override a virtual function, but your return value is not
the same as that of the function you're trying to override.
This is not allowed. All of: the name, arguments, and return
type must be identical.


No, they need not all be identical. Specifically, the
return type can vary, as long as, for an override in
a derived class, the return type has a base that is
the return type in the base class. That is what the
phrase 'covariant return types' means. For example,
this is legal C++:

class BR {};
class DR : public BR {};

class B {
public: virtual BR & covaret();
};
class D : public B {
public: virtual DR & covaret();
};
 
M

Martin Stettner

Mike said:
That is not the problem. The problem is that you're trying
to override a virtual function, but your return value is not
...
There's already an answer to this in the thread: The return type of a
derived function can be derived from the return type of the base function...
...
What specifically are you trying to do?

I'm trying to separate interface and implementation (I heard that's a
good thing :) ...)

IA and IB in the above code represent some interface classes to a
module. A and B implement these interfaces. From outside the module, a
client only knows that getIA returns an IA (and getIB and IB).

But inside the module, it would be nice to indicate that the methods
actually return the respective subclasses (if not, I'd either have to
implement a second method pair getA/getB or to do some nasty casting...)

Perhaps there's someone who can tell me how to solve the problem (or at
least tell me, that there's certainly no solution...)
 
B

Bogdan Sintoma

That's tricky :).
The standard says that the covariant return type shall be complete at
the point of declaration of the method in the derived class (or it can
be the derived class type)
I'm trying to separate interface and implementation (I heard that's a
good thing :) ...)
I heard that too :). But now I'm learning c# :(
Perhaps there's someone who can tell me how to solve the problem (or at
least tell me, that there's certainly no solution...)
Try this:
|struct IA
|{
| struct IB
| {
| virtual IA* getIA();
| };
| virtual IB* getIB();
|};
|typedef IA::IB IB;
|struct A : public IA
|{
| struct B : public IB
| {
| virtual A* getIA();
| };
| virtual B* getIB();
|};
|typedef A::B B;

Comeau c++ online say it's ok. VC 7.1 doesn't like it :).
....
Best regards,
Bogdan Sintoma
 
L

Larry Brasfield

Martin Stettner said:
There's already an answer to this in the thread: The return type of a derived function can be derived from the return type of the
base function...


I'm trying to separate interface and implementation (I heard that's a good thing :) ...)

Often it is, but not as a matter of dogma.
IA and IB in the above code represent some interface classes to a module. A and B implement these interfaces. From outside the
module, a client only knows that getIA returns an IA (and getIB and IB).

It seems to me that having the interface methods
return implementation classes rather than simply
the interface is confounding the boundary between
interface and implementation. Is there a particular
reason you need to blur that distinction?
But inside the module, it would be nice to indicate that the methods actually return the respective subclasses (if not, I'd either
have to implement a second method pair getA/getB or to do some nasty casting...)

You are free, inside the module, and within the
implementation class, to add methods and friends
as necessary to get the functionality you need
internally. The second method pair need not be
expensive. In fact, the interface implementation
can use that pair to do the real work.
Perhaps there's someone who can tell me how to solve the problem (or at least tell me, that there's certainly no solution...)

I am not expert enough to claim no solution exists, but I
could not think of any way to do exactly what you asked.
The problem is that the inheritance relationship cannot be
described independently of the class definition, so there
is an inherent circularity (as you have found) where each
subclass, to be defined as you desire, must have the other
class already defined. Without breaking the subclasses
into two, (separating out some more interface), there is
no way to break that circular dependency.

The answer, IMHO, is to get interface and implementation
considerations more cleanly separated, first in your own
thinking then in the code.
 
M

Martin Stettner

Larry said:
Mike said:
message

...
class IB;
class IA {
virtual IB* getIB() = 0;
};
class IB{
virtual IA* getIA() = 0;
};

class B;
class A : public IA{
B *getIB(); /// Here I get an error!
// If I replace with IB * getIB(); everything works fine
};

class B : public IB {
A* getIA();
};


[some snip]

Often it is, but not as a matter of dogma.

I agree with you on that point...
It seems to me that having the interface methods
return implementation classes rather than simply
the interface is confounding the boundary between
interface and implementation. Is there a particular
reason you need to blur that distinction?

I think I understand your arguments, but I'm not sure if I agree with
them: The interface is clearly defined using IA and IB (whose methods
also just return interface pointers). So no class using the interfaces
will have any clue about the implementation.

On the other hand, it's also well defined (in my case), that A and B
implement the methods in a way, that they always return objects of type
B and A respectively. And every part that works with the two
implementation classes knows about this.

So I would like to express this fact in code, and I think, that's one of
the reasons, covariant return types were introduced (but I may be wrong
on this last point).

[...]
internally. The second method pair need not be
expensive. In fact, the interface implementation
can use that pair to do the real work.

Actually, it wouldn't be expensive at all: The implementation just
returns an internal member pointer. But writing two methods which do
exactly the same thing the same way bothers me.
[...]
The problem is that the inheritance relationship cannot be
described independently of the class definition, so there

That's what I wasn't sure about: It could have been that there were some
syntax to do this kind of forward declaration for inheritances. But if
this is impossible, of course I have to find some other way.
...
The answer, IMHO, is to get interface and implementation
considerations more cleanly separated, first in your own
thinking then in the code.

I think, the way I want to do this is not so wrong. But this possibly
leads us to some sort of dogmatic discussion we both want do avoid here,
don't we?

But thank you very much for your thoughts, they push me to rethink my
design (which isn't a bad thing too, as i heard ...)

Martin

P.S: Sorry to the newsgroup purists: This subthread evolves away from a
language-specific topic, so I'll stop it here...
 
M

Martin Stettner

Bogdan said:
Martin Stettner wrote:

[...[

That's tricky :).

Isn't it? :)
The standard says that the covariant return type shall be complete at
the point of declaration of the method in the derived class (or it can
be the derived class type)

Would be nice to drop that restriction and instead allow to declare
incomplete types with inheritance relations... (what speaks against this
idea? But that's just a thought of mine, not a serious proposal, please
don't blame me for it...)
...
Try this:
|struct IA
|{
| struct IB
| {
| virtual IA* getIA();
| };
| virtual IB* getIB();
|};
|typedef IA::IB IB;
|struct A : public IA
|{
| struct B : public IB
| {
| virtual A* getIA();
| };
| virtual B* getIB();
|};
|typedef A::B B;
Wow, I wouldn't have come up with this! But sadly there's no good reason
why my IB should be a member of IA (if I'd use this, it'd have to mean
something...)
Comeau c++ online say it's ok.
Interesting: At the point of the declaration of B::getIA, A is not a
complete type, and getIA() is not a member of A (but of A::B). So,
conforms this to the standard?
VC 7.1 doesn't like it :).
Bad news, as this is the compiler I have to use for this project.
...
Best regards,
Bogdan Sintoma
Thanks a lot

Martin
 
B

Bogdan Sintoma

Martin said:
Would be nice to drop that restriction and instead allow to declare
incomplete types with inheritance relations... (what speaks against this
idea? But that's just a thought of mine, not a serious proposal, please
don't blame me for it...)

I think you should search (and maybe post this question) on
comp.std.c++.
Wow, I wouldn't have come up with this!
:)
I cannot take credit for this. I've saw that code a while ago on
c++.moderated.
Interesting: At the point of the declaration of B::getIA, A is not a
complete type, and getIA() is not a member of A (but of A::B). So,
conforms this to the standard?
I'm not sure.
The main problem here is that you cannot forward declare the
inheritance relationship. Since, in this example the type of A is well
known (at compile time) at the point of declaration of getIA, I think
it should be ok.

..
..
Bogdan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top