Multiple inheritance gives "ambigous resolution" error (C++)

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

scwizard
Posts: 519
Joined: Sun Mar 04, 2007 6:29 pm UTC
Location: New York City
Contact:

Multiple inheritance gives "ambigous resolution" error (C++)

Postby scwizard » Fri Feb 27, 2009 7:13 am UTC

So I have this data structure I'm implementing, and right now I'm mucking around with implementing all the diverse types of iterators.

Currently I have:

Code: Select all

class iterator :
public std::iterator<std::bidirectional_iterator_tag, T> {
   // Implementation of iterator stuff.
}

class reverse_iterator :
public iterator,
public std::reverse_iterator<iterator> {
   // Re-implementation of constructors and such.
   // Modified implementation of ++ and --.
   // Things such as operator* are unchanged, so I want to inherit them instead of reimplementing them.
}


Then in my code when I call:
std::cout << *TestObject.rbegin() << std::endl;

I get:
GCC: error: request for member 'operator*' is ambiguous
MSVSC++: error C2593: 'operator *' is ambiguous
could be 'char &shrub<T>::iterator::operator *(void)' [this is what I want it to be of course]
or could be 'char &std::_Revranit<_RanIt,_Base>::operator *(void) const' [wtf]

So what's the confusion? What could operator* possibly be other than the operator* from the iterator class I made?

I don't want to have to copy and paste the code for operator* each time I implement a new type of iterator. So what's the best way to fix this? Does this compile time error signify an error in my design?
~= scwizard =~

fazzone
Posts: 186
Joined: Wed Dec 10, 2008 9:38 pm UTC
Location: A boat

Re: Multiple inheritance gives "ambigous resolution" error (BASI

Postby fazzone » Fri Feb 27, 2009 11:30 am UTC

well the std::iterator class has an operator* for dereferencing...
*/

Karrion
Posts: 92
Joined: Fri Jun 22, 2007 12:14 am UTC
Location: Melbourne, AU

Re: Multiple inheritance gives "ambigous resolution" error (BASI

Postby Karrion » Fri Feb 27, 2009 12:40 pm UTC

Both std::iterator and std::reverse_iterator define an operator*; because your class extends both of those, the compiler can't determine which implementation you want your class to inherit.

scwizard
Posts: 519
Joined: Sun Mar 04, 2007 6:29 pm UTC
Location: New York City
Contact:

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby scwizard » Fri Feb 27, 2009 3:11 pm UTC

That's weird. Why would std::iterator define a operator*? std::iterator is only used for inheriting from I thought.

I guess I was wrong.

Well anyways, how do I go about fixing this? If the way I'm writing these iterators is the proper way, then I shouldn't need to have redundant operations.
~= scwizard =~

0xBADFEED
Posts: 687
Joined: Mon May 05, 2008 2:14 am UTC

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby 0xBADFEED » Fri Feb 27, 2009 3:38 pm UTC

You don't inherit from std::reverse_iterator. std::reverse_iterator is an adaptor over a bidirectional or random-access iterator to allow you to reverse the natural direction of the iterator.

To get a reverse_iterator you first create a bidirectional or random-access iterator. Then use the reverse iterator adaptor to get the reversed behavior for free.

So you might have your standard iterator like:

Code: Select all

template<typename T>
class MyIterator : public std::iterator<std::bidirectional_iterator_tag,T>
{
   //Iterator implementation
};

typedef std::reverse_iterator<MyIterator<T>> reverse_iterator;


The reverse iterator encloses your bidirectional iterator implementation and inverts the operations to give reversed traversal.

Here's some more information on iterator semantics

The reason you were getting the error is that both your custom iterator and the reverse_iterator implement operator*. The good news is that since you already have a bidirectional iterator you also have a reverse iterator.

scwizard
Posts: 519
Joined: Sun Mar 04, 2007 6:29 pm UTC
Location: New York City
Contact:

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby scwizard » Fri Feb 27, 2009 4:20 pm UTC

I'm trying that, and now when I go:

Code: Select all

shrub<char>::iterator Iter = TestObject.begin();
shrub<char>::reverse_iterator rIter = TestObject.rbegin();

std::list<shrub<char>::iterator*> empl;
empl.push_front(&Iter);
empl.push_front(&rIter);

(stolen from page 312 of TC++PL to help prove a point)

I get:
error C2664: 'std::list<_Ty>::push_front' : cannot convert parameter 1 from 'std::reverse_iterator<_RanIt> *' to 'shrub<T>::iterator &'

reverse_iterator is a type of iterator, and should behave as such.

EDIT: A question this whole thing raises, is why does std::iterator actually do stuff, instead of being a pure abstract class? (I think it's like that in Java)
Also why doesn't std::reverse_iterator inherit from std::iterator?
~= scwizard =~

0xBADFEED
Posts: 687
Joined: Mon May 05, 2008 2:14 am UTC

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby 0xBADFEED » Fri Feb 27, 2009 8:46 pm UTC

You're misunderstanding how C++ iterators work.

The std::iterator class doesn't define any functionality. It is only a traits class. All it does is define the traits tags that an iterator has. See here. Iterators are not inheritance based. They are trait based. The std algorithms that use iterators use type-traits and template-instantiation instead of inheritance to achieve polymorphism.

The reason you can't add a shrub<char>::reverse_iterator* to a list of shrub<char>::iterator* is that shrub<char>::iterator and shrub<char>::reverse_iterator are two completely different types.

I'll assume your 'shrub' class has roughly this form:

Code: Select all

template<typename T>
class shrub
{
    class iterator : public std::iterator<std::bidirectional_iterator_tag,T>
    { //iterator implementation};

   typedef std::reverse_iterator<iterator> reverse_iterator;

   //rest of shrub implementation
};


So you'll see that shrub<T>::iterator and shrub<T>::reverse_iterator are two completely different types.

The only thing that std::iterator does is define the standard typedefs that allow a generic algorithm to reason about the traits of the iterator, it provides no implementation functionality.

Java uses inheritance because its generics system is so much weaker than C++ and because that's what's natural in Java. I think the generics trait-based mechanism is more powerful if a bit more unwieldy and a bit more difficult to wrap your head around. This type of programming is something that is somewhat unique to C++ and you aren't likely to find many analogs in the OO world. It is very similar to constructs found in the functional world especially in languages like OCaml.

std::reverse_iterator doesn't inherit from std::iterator because it doesn't need to. If you understand the 'slicing' problem in C++ then it should be apparent as to why it is undesirable to use inheritance for iterator behavior. Specifically, because you want to pass iterator objects around as values instead of references.

Have you ever done this type of C++ programming before? Writing your own container class complete with iterators is a good way to get a fundamental grasp of modern C++ programming and make the transition from C-with-classes style C++ programming.

scwizard
Posts: 519
Joined: Sun Mar 04, 2007 6:29 pm UTC
Location: New York City
Contact:

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby scwizard » Sat Feb 28, 2009 12:44 am UTC

I haven't done this type of thing before. It's really helping me learn a lot.

In this case both iterator and reverse iterator have the same data, so slicing can't occur. Therefore I think it would be advantageous to have reverse_iterator inherit from iterator.

Reverse_iterator is a type of iterator, users should be able to treat it as such. There's no good reason they shouldn't be.

0xBADFEED wrote:The std::iterator class doesn't define any functionality.

Karrion wrote:Both std::iterator and std::reverse_iterator define an operator*

One of you is incorrect.
~= scwizard =~

0xBADFEED
Posts: 687
Joined: Mon May 05, 2008 2:14 am UTC

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby 0xBADFEED » Sat Feb 28, 2009 12:54 am UTC

Karrion is incorrect.

The 'ambiguous operator*' error you were getting was because you were inheriting shrub<T>::reverse_iterator from both std::reverse_iterator and your own custom shrub<T>::iterator. Both of which provided an implementation of operator*.

The std::iterator inheritance had nothing to do with that error.

sciwizard wrote:In this case both iterator and reverse iterator have the same data, so slicing can't occur.


Not if you're using a std::reverse_iterator. The data contained in a std::reverse_iterator is another iterator. If you use inheritance to model iterators the slicing occurs at the point that a std algorithm wants to return an iterator.

Therefore I think it would be advantageous to have reverse_iterator inherit from iterator. Reverse_iterator is a type of iterator, users should be able to treat it as such. There's no good reason they shouldn't be.


A reverse_iterator can be treated as an iterator. It is treated as an iterator. Just not through an inheritance relationship. I understand you're locked into thinking about polymorphism through inheritance. But the method used by the STL is actually more powerful and flexible than inheritance.

scwizard
Posts: 519
Joined: Sun Mar 04, 2007 6:29 pm UTC
Location: New York City
Contact:

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby scwizard » Sat Feb 28, 2009 1:17 am UTC

0xBADFEED wrote:A reverse_iterator can be treated as an iterator. It is treated as an iterator.

If it can be treated like an iterator, then why can't I go:
shrub<char>::iterator Iter = TestObjet.begin();
Iter = TestObject.rbegin();

That's the type of thing I want to make it able to do.

EDIT: Actually, let me check something one second.
~= scwizard =~

Rysto
Posts: 1460
Joined: Wed Mar 21, 2007 4:07 am UTC

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby Rysto » Sat Feb 28, 2009 1:31 am UTC

The mistake that you're making is assuming that there's some common base type defining the interface that an iterator implements, and all iterator classes inherit from this base type. There isn't. If you're familiar with dynamic languages like Python, then the best explanation is that iterators work through compile-time duck typing: all iterators define a common set of functions and operators, but there's no common base class to enforce this.

scwizard
Posts: 519
Joined: Sun Mar 04, 2007 6:29 pm UTC
Location: New York City
Contact:

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby scwizard » Sat Feb 28, 2009 1:34 am UTC

Ya, I was looking at the C++ algorithms, and they have no requirements that their arguments be of any type relating to std::iterator. It's all just templating.

So do I even need to have shrub<T>::iterator inherit from std::iterator? What advantages do I gain from doing so?
~= scwizard =~

0xBADFEED
Posts: 687
Joined: Mon May 05, 2008 2:14 am UTC

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby 0xBADFEED » Sat Feb 28, 2009 1:44 am UTC

It's by no means required.

The main thing is that it sets up the basic typedefs that are required by the iterator interface so that algorithms can reason about the iterator.

So by using std::iterator you can just specify the value type and the iteration category and it uses reasonable defaults for the others. Also, if you need to specify the other traits it's faster to just supply them to std::iterator than it is to write out the typedefs yourself.

scwizard
Posts: 519
Joined: Sun Mar 04, 2007 6:29 pm UTC
Location: New York City
Contact:

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby scwizard » Sat Feb 28, 2009 1:51 am UTC

0xBADFEED wrote:It's by no means required.

The main thing is that it sets up the basic typedefs that are required by the iterator interface so that algorithms can reason about the iterator.

So by using std::iterator you can just specify the value type and the iteration category and it uses reasonable defaults for the others. Also, if you need to specify the other traits it's faster to just supply them to std::iterator than it is to write out the typedefs yourself.

Oh I see how things are.

I think what I'm going to do is keep my iterator the way it is (inheriting from std::iterator). Then have my reverse iterator inherit solely from my iterator.
~= scwizard =~

0xBADFEED
Posts: 687
Joined: Mon May 05, 2008 2:14 am UTC

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby 0xBADFEED » Sat Feb 28, 2009 1:53 am UTC

Why not just use std::reverse_iterator?

Just do

Code: Select all

template<typename T>
class shrub
{
       struct iterator : public std::iterator<tag_t,T>{//iterator impl};

       typedef std::reverse_iterator<iterator> reverse_iterator;
       reverse_iterator rbegin() { return reverse_iterator(begin());}
       reverse_iterator rend() { return reverse_iterator(end());}
       //similarly for const_reverse_iterator
};


And you're done.

scwizard
Posts: 519
Joined: Sun Mar 04, 2007 6:29 pm UTC
Location: New York City
Contact:

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby scwizard » Sat Feb 28, 2009 1:58 am UTC

0xBADFEED wrote:Why not just use std::reverse_iterator?

Several reasons, the main one being that it's harder to turn it into a checked iterator because it wouldn't use the FirstLeaf dummy leaf as it's rend().
Actually scratch that. I could still make it checked fairly easily.

EDIT: Oh ya, I remember. It's because rposition(some number past the end) would not resolve to rend() if I used std::reverse_iterator.
Last edited by scwizard on Sat Feb 28, 2009 2:01 am UTC, edited 1 time in total.
~= scwizard =~

0xBADFEED
Posts: 687
Joined: Mon May 05, 2008 2:14 am UTC

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby 0xBADFEED » Sat Feb 28, 2009 2:01 am UTC

In any case I wouldn't make the reverse inherit from the forward. You could make them inherit from a common base that implements the common next/prev/deref functionality. Then the forward and reverse just provide the operator interface. But inheriting the reverse from the forward implementation is a bit unnatural.

scwizard
Posts: 519
Joined: Sun Mar 04, 2007 6:29 pm UTC
Location: New York City
Contact:

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby scwizard » Sat Feb 28, 2009 2:04 am UTC

0xBADFEED wrote:In any case I wouldn't make the reverse inherit from the forward. You could make them inherit from a common base that implements the common next/prev/deref functionality. Then the forward and reverse just provide the operator interface. But inheriting the reverse from the forward implementation is a bit unnatural.

Well if I had some private iterator_base, and I had iterator and reverse_iterator inherit from that, then I couldn't go:
shrub<char>::iterator Iter = TestObjet.begin();
Iter = TestObject.rbegin();

Inheriting a more specific implementation from the default implementation seems natural to me.

ADDITION:
What I'm trying to do now is get virtual functions to work.

Code: Select all

shrub<std::string>::iterator Iter = TestShrub.begin();

while(Iter != TestShrub.end()) {
   std::cout << *Iter << std::endl;
   ++Iter;
}

Iter = TestShrub.rbegin();

while(Iter != TestShrub.rend()) {
   std::cout << *Iter << std::endl;
   ++Iter; // Here I want the reverse_iterator version of operator++ to be called.
}

Now apparently I can only do this if I make Iter a pointer to an iterator. Why is this? How exactly do virtual functions work and why do they have this limitation?
~= scwizard =~

0xBADFEED
Posts: 687
Joined: Mon May 05, 2008 2:14 am UTC

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby 0xBADFEED » Sat Feb 28, 2009 2:22 am UTC

You can't.

You're slicing the iterator returned from rbegin(). What you have in Iter is an actual instance of shrub::iterator you can't reassign to a more specific type.

It's not anything to do with virtual functions. It's value vs. reference semantics. It's always a good idea to use value semantics with iterators. If you start trying to use reference semantics it's going to make your life much more difficult.

This might help

scwizard
Posts: 519
Joined: Sun Mar 04, 2007 6:29 pm UTC
Location: New York City
Contact:

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby scwizard » Sat Feb 28, 2009 2:28 am UTC

0xBADFEED wrote:You're slicing the iterator returned from rbegin()

I don't see how I'm slicing anything. Both iterator and reverse_iterator have the same data in this case. When I define reverse_iterator I don't define any data elements.

(this isn't reverse_iterator defined in terms of std::reverse_iterator, this is my own implementation)
~= scwizard =~

0xBADFEED
Posts: 687
Joined: Mon May 05, 2008 2:14 am UTC

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby 0xBADFEED » Sat Feb 28, 2009 2:41 am UTC

I'm using the term 'slicing' in a somewhat extended sense. Not that it's slicing data off but that it is treating the reverse_iterator as an instance of 'iterator'. Because 'Iter' is actually an object of type 'iterator' what is being invoked is the assignment operator of 'iterator', i.e. iterator::operator=(const iterator& other).

What you have there with 'Iter' is an actual object of type 'iterator' you cannot reassign it to a derived class.

Define an assignment operator on 'iterator' and have it print something out when it is called or step into it. You'll see what I'm talking about.

scwizard
Posts: 519
Joined: Sun Mar 04, 2007 6:29 pm UTC
Location: New York City
Contact:

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby scwizard » Sat Feb 28, 2009 2:53 am UTC

Code: Select all

shrub<std::string>::iterator Iter = TestShrub.begin();

while(Iter != TestShrub.end()) {
   std::cout << *Iter << std::endl;
   ++Iter;
}

Iter = TestShrub.rbegin();

while(Iter != TestShrub.rend()) {
   std::cout << *Iter << std::endl;
   ++Iter; // Here I want the reverse_iterator version of operator++ to be called.
}

It would be possible to make this work through mad scheme involving weird operator overloading.

But I won't do that.
~= scwizard =~

0xBADFEED
Posts: 687
Joined: Mon May 05, 2008 2:14 am UTC

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby 0xBADFEED » Sat Feb 28, 2009 3:02 am UTC

Why are you even trying to do this? It's very non-standard and goes against the common iterator conventions. If you need a reverse iterator just initialize a reverse iterator off of an existing bidirectional iterator.

scwizard
Posts: 519
Joined: Sun Mar 04, 2007 6:29 pm UTC
Location: New York City
Contact:

Re: Multiple inheritance gives "ambigous resolution" error (C++)

Postby scwizard » Sat Feb 28, 2009 3:23 am UTC

I'm not trying to do that anymore. I see how things work now and I won't try doing anything too weird.
~= scwizard =~


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 3 guests