Forums

Skip to content

Advanced search
  • Quick links
    • Unanswered topics
    • Active topics
    • Search
  • FAQ
  • Login
  • Register
  • Board index Assistance Portage & Programming
  • Search

C++ multiple inheritance ambiguity

Problems with emerge or ebuilds? Have a basic programming question about C, PHP, Perl, BASH or something else?
Post Reply
Advanced search
19 posts • Page 1 of 1
Author
Message
dmitchell
Veteran
Veteran
User avatar
Posts: 1159
Joined: Sat May 17, 2003 4:51 pm
Location: Austin, Texas

C++ multiple inheritance ambiguity

  • Quote

Post by dmitchell » Tue May 23, 2006 2:39 am

Code: Select all

struct PrivateBase { };
struct PublicBase { };

void foo( PrivateBase ) { }
void foo( PublicBase ) { }

struct Derived : PublicBase, private PrivateBase { };

int main()
{
  Derived d;
  foo( d ); // ambiguous
}
Why can't the call to foo() resolve to foo( PublicBase )? Any suggestions for a work-around? Keep reading for more detail.

Code: Select all

struct PrivateBase { };

void foo( PrivateBase const& ) { }
void bar( PrivateBase const& ) { }
// Many more functions...

template<class PrivateBase>
struct PublicBase { };

template<class PrivateBase>
void foo( PublicBase<PrivateBase> const& x )
{ foo( (PrivateBase const&) x ); }

template<class PrivateBase>
void bar( PublicBase<PrivateBase> const& x )
{ bar( (PrivateBase const&) x ); }

// Many more functions...

struct Derived : PublicBase<PrivateBase>, private PrivateBase { };

int main()
{
  Derived d;
  foo( d );
  bar( d );
  // ...
}
The idea is that by inheriting from PublicBase<PrivateBase>, the author of Derived doesn't need to implement friend foo(), bar(), etc. that simply forward to foo( PrivateBase ), bar( PrivateBase ), etc..

On second thought, I wonder if the above would work even if the function calls resolved the way I want them to. Any suggestions on how to achieve what I want?
Last edited by dmitchell on Tue May 23, 2006 6:23 am, edited 1 time in total.
Your argument is invalid.
Top
ciaranm
Retired Dev
Retired Dev
User avatar
Posts: 1719
Joined: Sat Jul 19, 2003 11:04 pm
Location: In Hiding
Contact:
Contact ciaranm
Website

Re: C++ multiple inheritance ambiguity

  • Quote

Post by ciaranm » Tue May 23, 2006 2:51 am

dmitchell wrote:Why can't the call to foo() resolve to foo( PublicBase )? Any suggestions for a work-around? Keep reading for more detail.
Public/protected/private isn't considered when picking out candiate functions. It's a major pain in the ass. You can do some very sneaky tricks with templates to avoid it, but it's generally not worth it.
On second thought, I wonder if the above would work even if the function calls resolved the way I want them to. Any suggestions on how to achieve what I want?
What the heck are you trying to do? I suspect you're massively overcomplicating things somehow...
Paludis 0.12, 127.35% Portage compatible and six times faster.
Top
dmitchell
Veteran
Veteran
User avatar
Posts: 1159
Joined: Sat May 17, 2003 4:51 pm
Location: Austin, Texas

Re: C++ multiple inheritance ambiguity

  • Quote

Post by dmitchell » Tue May 23, 2006 3:42 am

ciaranm wrote:Public/protected/private isn't considered when picking out candiate functions. It's a major pain in the ass. You can do some very sneaky tricks with templates to avoid it, but it's generally not worth it.
Damn. OK, I'll ruminate on that.
What the heck are you trying to do? I suspect you're massively overcomplicating things somehow...
Perhaps. First, note that the public interface to PrivateBase is implemented in terms of the free functions foo(), bar(), etc.. Derived is implemented in terms of--but is not interchangeable with!--PrivateBase, and offers a similar interface. For the most part, calls to foo( Derived ), bar( Derived ), etc. should simply return foo( PrivateBase ), bar( PrivateBase ), and so on. One possibility is

Code: Select all

class Derived : PrivateBase {
  PrivateBase const& base_reference() const
  { return *this; }

  friend void foo( Derived const& x )
  { return foo( x.base_reference() ); }

  friend void bar( Derived const& x )
  { return bar( x.base_reference() ); }

  // More...
};
This is OK, but it's annoying because there are many functions that need to be forwarded in this manner. Worse, these forwarding functions will have to be written for every class that derives from PrivateBase.

My idea is to overload these functions for PublicBase, and derive Derived from PublicBase<PrivateBase>. Then a call to foo() such as

Code: Select all

Derived d;
foo( d );
will match foo( PublicBase<PrivateBase> const& ), the PublicBase<PrivateBase> reference will be casted to a PrivateBase reference, and foo( PrivateBase const& ) will be called. I wouldn't have to write all the forwarding functions each time I derive a new class from PrivateBase.
Your argument is invalid.
Top
wellwhoopdedooo
n00b
n00b
Posts: 69
Joined: Fri Mar 04, 2005 6:06 pm

  • Quote

Post by wellwhoopdedooo » Tue May 23, 2006 3:50 am

The simple solution appears to be just giving them different function names.

Or, if they're sufficiently similar, a templated function that you explicitly type when you call it, e.g. foo<PublicBase>(d); You could even make explicit specializations if all you're really looking for is a function with the same name that does different things... but that's probably not a good idea.

But really, multiple inheritence usually isn't the answer anyway. Are you absolutely sure that Derived is_a(PublicBase) and at the same time is_a(PrivateBase)? Private inheritance is very limiting, and requires your derived class to act as a mediator to the private base class anyway, so you're probably better off making that a data member. It shouldn't be any more work--probably less. If you're using multiple inheritence, one or more likely both classes should be pure virtual. There are exceptions... but not many.
Top
ciaranm
Retired Dev
Retired Dev
User avatar
Posts: 1719
Joined: Sat Jul 19, 2003 11:04 pm
Location: In Hiding
Contact:
Contact ciaranm
Website

  • Quote

Post by ciaranm » Tue May 23, 2006 3:54 am

Actually, it looks to me like you should be using member functions rather than free functions...
Paludis 0.12, 127.35% Portage compatible and six times faster.
Top
wellwhoopdedooo
n00b
n00b
Posts: 69
Joined: Fri Mar 04, 2005 6:06 pm

Re: C++ multiple inheritance ambiguity

  • Quote

Post by wellwhoopdedooo » Tue May 23, 2006 3:59 am

dmitchell wrote:Perhaps. First, note that the public interface to PrivateBase is implemented in terms of the free functions foo(), bar(), etc.. Derived is implemented in terms of--but is not interchangeable with!--PrivateBase, and offers a similar interface.
I'm curious, why is that? OOP is all about encapsulation. Functions and data, together... forever <3. It's beautiful, man.

I can't tell for certain, but it also doesn't sound like Derived should be inheriting from PrivateBase.
Top
wellwhoopdedooo
n00b
n00b
Posts: 69
Joined: Fri Mar 04, 2005 6:06 pm

  • Quote

Post by wellwhoopdedooo » Tue May 23, 2006 4:00 am

ciaranm wrote:Actually, it looks to me like you should be using member functions rather than free functions...
Aargh, beat to the punch again!
Top
dmitchell
Veteran
Veteran
User avatar
Posts: 1159
Joined: Sat May 17, 2003 4:51 pm
Location: Austin, Texas

  • Quote

Post by dmitchell » Tue May 23, 2006 4:03 am

wellwhoopdedooo wrote:The simple solution appears to be just giving them different function names.
Unfortunately that's not an option. Derived and PrivateBase both model a concept that requires foo(), etc. as valid expressions.
Or, if they're sufficiently similar, a templated function that you explicitly type when you call it
Also not an option for the same reason as above.
Are you absolutely sure that Derived is_a(PublicBase) and at the same time is_a(PrivateBase)?
Derived isn't a PrivateBase, it's just conveniently implemented in terms of PrivateBase. As for PublicBase, it would be an empty class--it's only purpose is to make the friend forwarding functions for Derived unnecessary.
Your argument is invalid.
Top
dmitchell
Veteran
Veteran
User avatar
Posts: 1159
Joined: Sat May 17, 2003 4:51 pm
Location: Austin, Texas

  • Quote

Post by dmitchell » Tue May 23, 2006 4:04 am

ciaranm wrote:Actually, it looks to me like you should be using member functions rather than free functions...
The free functions are required by a concept.
Your argument is invalid.
Top
dmitchell
Veteran
Veteran
User avatar
Posts: 1159
Joined: Sat May 17, 2003 4:51 pm
Location: Austin, Texas

  • Quote

Post by dmitchell » Tue May 23, 2006 4:10 am

It might be helpful to view Derived as being similar to STL container adaptors: std::queue is implemented in terms of std::deque, but isn't a std::deque. Derived derives from PrivateBase (as opposed to aggregating a PrivateBase) because 1) I hope to do what I've described above, and 2) PrivateBase offers a protected interface that I'd like to take advantage of in Derived.
Your argument is invalid.
Top
wellwhoopdedooo
n00b
n00b
Posts: 69
Joined: Fri Mar 04, 2005 6:06 pm

  • Quote

Post by wellwhoopdedooo » Tue May 23, 2006 4:11 am

PrivateBase is the implementation, right? If so, that's all good.

But what does PublicBase do again? If it's an empty class, why do you need overloaded functions that operate on both it and PrivateBase? And if you do actually need that, then your call to that function is definitely ambiguous, I as a person can't even think of how to tell which one should be called.
Top
wellwhoopdedooo
n00b
n00b
Posts: 69
Joined: Fri Mar 04, 2005 6:06 pm

  • Quote

Post by wellwhoopdedooo » Tue May 23, 2006 4:16 am

dmitchell wrote:It might be helpful to view Derived as being similar to STL container adaptors: std::queue is implemented in terms of std::deque, but isn't a std::deque. Derived derives from PrivateBase (as opposed to aggregating a PrivateBase) because 1) I hope to do what I've described above, and 2) PrivateBase offers a protected interface that I'd like to take advantage of in Derived.
Not sure what you mean by "implemented in terms of". std::queue definitely doesn't inherit from std::deque. It does, however, have std::deque as a data member by default, although that's selectable.
Top
dmitchell
Veteran
Veteran
User avatar
Posts: 1159
Joined: Sat May 17, 2003 4:51 pm
Location: Austin, Texas

  • Quote

Post by dmitchell » Tue May 23, 2006 4:19 am

wellwhoopdedooo wrote:PrivateBase is the implementation, right? If so, that's all good.
Yes.
But what does PublicBase do again? If it's an empty class, why do you need overloaded functions that operate on both it and PrivateBase?
Derived derives publicly from PublicBase<PrivateBase> so that a call such as

Code: Select all

Derived d;
foo( d );
will match

Code: Select all

template<class PrivateBase>
void foo( PublicBase<PrivateBase> const& x )
{ foo( (PrivateBase const&) x ); }
The idea is that since x is really a reference to a Derived, it can be treated as a reference to a PrivateBase.
Last edited by dmitchell on Tue May 23, 2006 6:25 am, edited 2 times in total.
Your argument is invalid.
Top
dmitchell
Veteran
Veteran
User avatar
Posts: 1159
Joined: Sat May 17, 2003 4:51 pm
Location: Austin, Texas

  • Quote

Post by dmitchell » Tue May 23, 2006 4:22 am

wellwhoopdedooo wrote:Not sure what you mean by "implemented in terms of".
If you have The C++ Programming Language, Third Edition, look at page 477. The queue member functions simply call members of the underlying deque (or whatever).
Your argument is invalid.
Top
wellwhoopdedooo
n00b
n00b
Posts: 69
Joined: Fri Mar 04, 2005 6:06 pm

  • Quote

Post by wellwhoopdedooo » Tue May 23, 2006 4:33 am

Part of the point of an implementation is that it doesn't matter which implementation you use, so you shouldn't be in the situation of having to care that your class is_a(PrivateBase). If you find yourself in that situation, unfortunately, you're probably doing something wrong. If you have functions that do depend on the implementation of PrivateBase, they should be member functions of PrivateBase. It may not be the prettiest thing in the world, but aThing.doSomethingTo() is probably a better solution. If you really want it to read well and go doSomethingTo(aThing) you can make that a friend function.

I do have the book, and when I opened it, it opened on that exact page. Weird. Anyway, it does call the members, but c is a protected data member, and not a base class. That means that you can't tell if queue has a deque inside it. Well, you can, but only because it's templated. But it also means that you don't have to care; you know the class is taking care of the details because you can't.
Top
dmitchell
Veteran
Veteran
User avatar
Posts: 1159
Joined: Sat May 17, 2003 4:51 pm
Location: Austin, Texas

  • Quote

Post by dmitchell » Tue May 23, 2006 4:47 am

Here's a working implementation of (almost) what I want to do. The trouble is that when I change the name of foo2() to foo(), I get the error `PrivateBase' is an inaccessible base of `Derived'. What I want is for a call to foo( d ) to resolve to

Code: Select all

foo( PublicBase<PrivateBase> const& )
not

Code: Select all

foo( PrivateBase const& )

Code: Select all

#include <iostream>

struct PrivateBase { };

void foo( PrivateBase const& x )
{ std::cout << "Here" << std::endl; }

template<class PrivateBase>
struct PublicBase { };

template<class PrivateBase>
void foo2( PublicBase<PrivateBase> const& x )
{ foo( (PrivateBase const&) x ); }

struct Derived : PublicBase<PrivateBase>, private PrivateBase { };

int main()
{
  Derived d;
  foo2( d ); // Prints "Here"
}
Your argument is invalid.
Top
wellwhoopdedooo
n00b
n00b
Posts: 69
Joined: Fri Mar 04, 2005 6:06 pm

  • Quote

Post by wellwhoopdedooo » Tue May 23, 2006 4:58 am

I really think you need to approach this from a different angle. Those templates are some serious spaghetti. I can't even imagine what you're actually trying to do, and I've seen and written some pretty convoluted template code. I think you need to walk up your program's structure until you have these things as concepts that you can separate from the rest of your structure, prune them, and start over with something way simpler.
Top
dmitchell
Veteran
Veteran
User avatar
Posts: 1159
Joined: Sat May 17, 2003 4:51 pm
Location: Austin, Texas

  • Quote

Post by dmitchell » Tue May 23, 2006 5:19 am

wellwhoopdedooo wrote:I really think you need to approach this from a different angle. [...] I think you need to walk up your program's structure until you have these things as concepts that you can separate from the rest of your structure, prune them, and start over with something way simpler.
If I can't make this work I'll just use the friend function approach I outlined above. Annoying, but ultimately just a matter of copy/paste and search/replace.
Those templates are some serious spaghetti. I can't even imagine what you're actually trying to do, and I've seen and written some pretty convoluted template code.
I have an implicit graph structure (PrivateBase) for use with the Boost Graph Library that models a bunch of graph concepts and has some extra functionality. The functions foo(), bar(), and so on are the valid expressions required by these graph concepts. I have a second graph structure (Derived) that is roughly the same as the first one but it hides the extra functionality from the user. The first class is highly configurable by the user, this one is not; in a sense it is a special case of the first class. Valid expressions invoked on this derived class should simply be forwarded to the base class, and I'd like this forwaring to be automatic and not require defining a bunch of trivial friend functions.
Your argument is invalid.
Top
dmitchell
Veteran
Veteran
User avatar
Posts: 1159
Joined: Sat May 17, 2003 4:51 pm
Location: Austin, Texas

  • Quote

Post by dmitchell » Fri May 26, 2006 1:32 am

In the words of Scott Meyers, I find myself "in the happy circumstance of having no choice whatsoever." The language rules stipulate that even though PrivateBase is not accessible, it is still visible, and therefore

Code: Select all

void foo( PrivateBase )
is a better match than the template function

Code: Select all

template<class PrivateBase>
void foo( PublicBase<PrivateBase> )
I'm not sure I would have designed the language that way, but there it is. I'll simply use the friend function approach outlined above.

Thanks for the feedback.
Your argument is invalid.
Top
Post Reply

19 posts • Page 1 of 1

Return to “Portage & Programming”

Jump to
  • Assistance
  • ↳   News & Announcements
  • ↳   Frequently Asked Questions
  • ↳   Installing Gentoo
  • ↳   Multimedia
  • ↳   Desktop Environments
  • ↳   Networking & Security
  • ↳   Kernel & Hardware
  • ↳   Portage & Programming
  • ↳   Gamers & Players
  • ↳   Other Things Gentoo
  • ↳   Unsupported Software
  • Discussion & Documentation
  • ↳   Documentation, Tips & Tricks
  • ↳   Gentoo Chat
  • ↳   Gentoo Forums Feedback
  • ↳   Duplicate Threads
  • International Gentoo Users
  • ↳   中文 (Chinese)
  • ↳   Dutch
  • ↳   Finnish
  • ↳   French
  • ↳   Deutsches Forum (German)
  • ↳   Diskussionsforum
  • ↳   Deutsche Dokumentation
  • ↳   Greek
  • ↳   Forum italiano (Italian)
  • ↳   Forum di discussione italiano
  • ↳   Risorse italiane (documentazione e tools)
  • ↳   Polskie forum (Polish)
  • ↳   Instalacja i sprzęt
  • ↳   Polish OTW
  • ↳   Portuguese
  • ↳   Documentação, Ferramentas e Dicas
  • ↳   Russian
  • ↳   Scandinavian
  • ↳   Spanish
  • ↳   Other Languages
  • Architectures & Platforms
  • ↳   Gentoo on ARM
  • ↳   Gentoo on PPC
  • ↳   Gentoo on Sparc
  • ↳   Gentoo on Alternative Architectures
  • ↳   Gentoo on AMD64
  • ↳   Gentoo for Mac OS X (Portage for Mac OS X)
  • Board index
  • All times are UTC
  • Delete cookies

© 2001–2026 Gentoo Foundation, Inc.

Powered by phpBB® Forum Software © phpBB Limited

Privacy Policy

 

 

magic