473,405 Members | 2,334 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,405 software developers and data experts.

Ambiguity issue

200 100+
Hello everyone,


I think compiler is too strict in this case. You can see, no data member, no virtual function.

I am using Visual Studio 2008. The compiler error is simply because of Diamond pattern -- duplicate base class? Actually from logical point of view, there should be no ambiguity issue.

Any ideas?

Expand|Select|Wrap|Line Numbers
  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. class Base {
  6. public:
  7.     int foo() {cout << "Base" << endl;}
  8. };
  9.  
  10. class Derived1: public Base
  11. {
  12. public:
  13.     int foo() {cout << "Derived1" << endl;}
  14. };
  15.  
  16. class Derived2: public Base
  17. {
  18. public:
  19.     int foo() {cout << "Derived2" << endl;}
  20. };
  21.  
  22. class Final: public Derived1, public Derived2 {
  23. public:
  24.     int foo() {cout << "Final" << endl;}
  25. };
  26.  
  27. int main()
  28. {
  29.     Final f;
  30.     Final* p = &f;
  31.     Base* pb = p; //error C2594: 'initializing' : ambiguous conversions from 'Final *' to 'Base *'
  32.     return 0;
  33. }
  34.  

thanks in advance,
George
Feb 5 '08 #1
13 2638
ashitpro
542 Expert 512MB
Hello everyone,


I think compiler is too strict in this case. You can see, no data member, no virtual function.

I am using Visual Studio 2008. The compiler error is simply because of Diamond pattern -- duplicate base class? Actually from logical point of view, there should be no ambiguity issue.

Any ideas?

Expand|Select|Wrap|Line Numbers
  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. class Base {
  6. public:
  7.     int foo() {cout << "Base" << endl;}
  8. };
  9.  
  10. class Derived1: public Base
  11. {
  12. public:
  13.     int foo() {cout << "Derived1" << endl;}
  14. };
  15.  
  16. class Derived2: public Base
  17. {
  18. public:
  19.     int foo() {cout << "Derived2" << endl;}
  20. };
  21.  
  22. class Final: public Derived1, public Derived2 {
  23. public:
  24.     int foo() {cout << "Final" << endl;}
  25. };
  26.  
  27. int main()
  28. {
  29.     Final f;
  30.     Final* p = &f;
  31.     Base* pb = p; //error C2594: 'initializing' : ambiguous conversions from 'Final *' to 'Base *'
  32.     return 0;
  33. }
  34.  

thanks in advance,
George
Well..
This is a typical problem of multiple inheritance.
Have a look at this statement:

class Final: public Derived1, public Derived2

You are inheriting through Derived1 and Derived2.
And very imp thing is that both of them were derived from Base.
So Final class will receive two copies of Base. One from Derived1 and other from Derived2. Thus ambiguity has to happened.
When you use such type of construct don't forget to add "virtual" word in every child of a Base. Here your code must look like.

class Derived1: virtual public Base

class Derived2: virtual public Base

Note:"virtual" word may appear after "public".
Feb 5 '08 #2
hdanw
61
This was taken from the MSDN .net 2003 documentation

'operator' : ambiguous conversions from 'type1' to 'type2'
No conversion from type1 to type2 was more direct than any other. One possible solution is to define or specify an explicit conversion.
The following sample generates C2594:
// C2594.cpp
// compile with: /c
struct A{};
struct I1 : A {};
struct I2 : A {};
struct D : I1, I2 {};
A *f (D *p) {
return (A*) (p); // C2594
// try the following line instead
// return reinterpret_cast<A*> (p);
}
Does this make since to you?

Just what are you trying to do anyway. I bet you could save some time asking different question rather than trial and error. But then again that is how I learned.

keep in mind that there are not multiple entries in the function table for the object Final, and for the function foo(). There is only one.

the object pb points to a different function table, and will use that instance of foo();

change some code to this
Expand|Select|Wrap|Line Numbers
  1. Base* pb = reinterpret_cast<Base*> (p);
  2.  
  3. f.foo();
  4.  
  5. p->foo();
  6.  
  7. pb pb->foo();
  8.  
  9.  
you should see :

Final
Final
Base

Also you need to drop the "int" or add a return value to your foo functions.

Now change base class to
Expand|Select|Wrap|Line Numbers
  1. class Base 
  2. {
  3.     public:
  4.  
  5.     virtual int foo() {cout << "Base" << endl; return 0;}
  6.  
  7. };
  8.  
  9.  
Now look at what you get.

The base class does point to all instances of foo();

So the Base object pb calls the function foo stored in the function table for the object 'f' .

you should see:

Final
Final
Final

If you'd like to see some powerfull objects in cpp I have loads of code you could look at.

Dan -
Feb 5 '08 #3
hdanw
61
Well..
You are inheriting through Derived1 and Derived2.
And very imp thing is that both of them were derived from Base.
So Final class will receive two copies of Base. One from Derived1 and other from Derived2. .
Thats not true;

Both Derived1 and Derived2 have foo that is true, but once he declared a new instance of the function foo(), both of those methods are overridden with the local definition.

Take out the local definition and see what happens.

Dan -
Feb 5 '08 #4
ashitpro
542 Expert 512MB
Thats not true;

Both Derived1 and Derived2 have foo that is true, but once he declared a new instance of the function foo(), both of those methods are overridden with the local definition.

Take out the local definition and see what happens.

Dan -
Thanks for reply Dan.
Have a look at this link.

http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.vacpp7a.doc/language/ref/clrc14cplr135.htm

I think It has nothing to do with foo() function.
The problem is here because of Final receives two copies of Base.
More precisely, we are not even calling any functions.
What I thought was the concept of Virtual Base Class.
Please..explain this thing more deeply.
Feb 5 '08 #5
hdanw
61
Thanks for reply Dan.
Have a look at this link.

http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.vacpp7a.doc/language/ref/clrc14cplr135.htm

I think It has nothing to do with foo() function.
The problem is here because of Final receives two copies of Base.
More precisely, we are not even calling any functions.
What I thought was the concept of Virtual Base Class.
Please..explain this thing more deeply.
I wrote a number of classess I though were great they all interacted the same way, kept things simple. well then i tried to derive a class that came from 2 of the previous classes, and the same situation occurs where the interface is the same from each.

Something I definately avoid now.

But what is happening is at compiler time.

Since the two derived classes versions of foo are overridden, they dont exist in Final. and thus not the cuase of "THIS" problem.

But keep programming that way and sooner or later your world will crash.

The ambiguity from

Base dp = (p);

is becuase the compiler has no directive to decide whether to use the foo from final, or the foo from Base.

the object p at runtime has a function table associated with it that points to the foo that was assigned at declaration :

Final * p = ...

thus p->foo points to Final.foo

and the base object pb treats the assigned pointer as if it were a pointer to Base, so naturally the compiler assumes that

pb->foo will point to Base->foo, and it does.

So now the compiler has a pointer to a Base:foo that is actually declared as a deferent function Final:foo.

We have a ... but we are calling it a ...,

so the compiler responds with, what the *ell are you doing, do you want me to use ... or ...

We can shut the compiler up by telling to act like the object p is a pointer to Base and not Final. And thus the compiler links to the function table associated with Base, or Base:foo to be exact.

When we declare foo as a virtual function, this tells the compiler that there will be new version of foo coming, and that one should always take precedent.

So The compiler uses the function table associated with the pointer p
and when invoked calls Final:foo;

There are a lot of tricky situations when dealing with multiple inheritance and the combination of virtual and hidden functions.
Hidden functions don't always operate the way you'd like them to.

For example calling a hidden function from within a virtual function sometimes calls the function that you though was hidden. And not the function doing the hiding.

Using virtual keyword is better to garantee that the overloaded function is not being called.
Feb 5 '08 #6
hdanw
61
if we comment out the overriding foo in final like this

Expand|Select|Wrap|Line Numbers
  1. class Final:public Derived1, public Derived2 {
  2.  
  3. public:
  4.  
  5. // int foo() {cout << "Final" << endl;return 0;}
  6.  
  7. };
  8.  
  9.  
then we get the compiler error

c:\delme\tempme\tempme.cpp(36): error C2385: ambiguous access of 'foo' in 'Final'


becuase, very straight forward, the compiler does not know whether to use Derived1:foo or Derived2:foo

And this will not compile no matter what you do.

A lot of newbies try to derive classes, upon classes.
They need to learn to move "base" classes to the members section.

They need to be able to answer:

What is an aggregate? and when do I use one?

The answer is a collection, like a box full. Not a growth like a tree.

Use them more often than not.

For example you might say that a car has an engine, and a gas can
Expand|Select|Wrap|Line Numbers
  1. class car
  2. {
  3. public:
  4. engine enginetype;
  5. fueltank reserve;
  6. }
  7.  
versus
Expand|Select|Wrap|Line Numbers
  1. class car:public engine, public fueltank
  2. {
  3. }
  4.  
Which is the aggregate?

you might have a a function called start in engine definition so that the first would be

car:engine:start(), while the second is car:start(); Which sounds better.

But believe me, it is better to use the first example, and then define a function start that acts on the member engine,
car:start(); // that sound good

The general rule of thumb is if its not ever going to act like a ... then it should not be derived from ...

for example you cant use the car like a gas can, so

class car:public fueltank{}

doesn't make much since

however if you want to modify a car class and the sub class does everything the original did, and can be used just like the base class car then it makes since

class ferrari: public car{}

Thats oop in a nut shell.

Happy coding.

Dan -
Feb 5 '08 #7
weaknessforcats
9,208 Expert Mod 8TB
Base* pb = p; //error C2594: 'initializing' : ambiguous conversions from 'Final *' to 'Base *'
In this case the compiler can't tell which Base* to conver to: The one from Derived1 or the one from Derived2. I'm sure tou are aware that there are two base objects in your final object.

This is exactly the design of the IUknown of COM. But Microsoft does not do stuff like this. Instead, they developed a QueryInterface() that works around this.
Feb 5 '08 #8
George2
200 100+
Hi Dan,


Can you show your completed code for how do you get the output,

--------------------
Final
Final
Final
--------------------

I can not understand how you make the pointer to Base pointing to the function table of Final?

So the Base object pb calls the function foo stored in the function table for the object 'f' .

you should see:

Final
Final
Final

If you'd like to see some powerfull objects in cpp I have loads of code you could look at.

Dan -

regards,
George
Feb 7 '08 #9
George2
200 100+
Hi hdanw,


Great reply! But I am not 100 sure about your samples.

In your below description, the code,

Base dp = (p);

should not compile not because of ambiguity issue, but syntax issue (left side object type, right side pointer type)

"We can shut the compiler up " -- what your compiler errors?

Again, it is appreciated if you could post your code and compiler error messages here to make your points more clear.


The ambiguity from

Base dp = (p);

is becuase the compiler has no directive to decide whether to use the foo from final, or the foo from Base.

the object p at runtime has a function table associated with it that points to the foo that was assigned at declaration :

Final * p = ...

thus p->foo points to Final.foo

and the base object pb treats the assigned pointer as if it were a pointer to Base, so naturally the compiler assumes that

pb->foo will point to Base->foo, and it does.

So now the compiler has a pointer to a Base:foo that is actually declared as a deferent function Final:foo.

We have a ... but we are calling it a ...,

so the compiler responds with, what the *ell are you doing, do you want me to use ... or ...

We can shut the compiler up by telling to act like the object p is a pointer to Base and not Final. And thus the compiler links to the function table associated with Base, or Base:foo to be exact.

regards,
George
Feb 7 '08 #10
hdanw
61
You are correct, I thought I included that.

The code should read

Expand|Select|Wrap|Line Numbers
  1. Base* pb  = reinterpret_cast<Base*> (p);
  2.  
This resolves the initial problem. But again as soon as you comment out the foo definition in final, you get the compiler error that ashitpro was indicating.

To get the
Final
Final
Final

we declare the base class foo as virtual:
Expand|Select|Wrap|Line Numbers
  1. class Base {
  2. public:
  3.     virtual int foo() {cout << "Base" << endl; return 0;}
  4. };
  5.  
AS per your request the intire program is here:
Expand|Select|Wrap|Line Numbers
  1. #include <iostream>
  2. #include <stdafx.h> 
  3.  
  4. using namespace std;
  5.  
  6. class Base {
  7. public:
  8.     virtual int foo() {cout << "Base" << endl; return 0;}
  9. };
  10.  
  11. class Derived1: public Base
  12. {
  13. public:
  14.     int foo() {cout << "Derived1" << endl;return 0;}
  15. };
  16.  
  17. class Derived2: public Base
  18. {
  19. public:
  20.      int foo() {cout << "Derived2" << endl;return 0;}
  21. };
  22.  
  23. class Final: public Derived1, public Derived2 {
  24. public:
  25.   int foo() {cout << "Final" << endl;return 0;}
  26. };
  27.  
  28. int main()
  29. {
  30.     Final f;
  31.     Final* p = &f;
  32.     //Base* pb = (p); //error C2594: 'initializing' : ambiguous conversions from 'Final *' to 'Base *'
  33.     Base* pb = reinterpret_cast<Base*> (p);
  34.     f.foo();
  35.     p->foo();
  36.     pb->foo();
  37.  
  38.     return 0;
  39. }
  40.  
Happy Coding
Feb 8 '08 #11
hdanw
61
You need to understand the at run time a pointer is just that, but at compile time the pointer is 1- a pointer with size and location, and 2 - pointer to Something.

The type information exists so that the compiler can do a number of things that they normally wouldn't.

For example.

Expand|Select|Wrap|Line Numbers
  1.  
  2. struct somestruct
  3. {
  4.     int x, y, z;
  5.     float a, b;
  6. }
  7. somestruct mytable[64];
  8.  
  9. (...)
  10.  
  11. somestruct * ptable = mytable;
  12.  
  13. ptable++;
  14.  
In the above code, the compiler knows that ptable is a pointer, and to a somestruct object. The ++ on the pointer instructs the compiler to calculate the offset to the next intry in mytable.

it is equivalent to mytable[1];

As a result a pointer to apples cannot be treated the same as a pointer to oranges.

Happy Coding.

Dan -
Feb 8 '08 #12
George2
200 100+
Hi hdanw,


Why in your below code,

pb->foo();

will output Final other than output Base? I have tested that the output is Final.

My question is
1. pb points Final instance, and the foo in Final is not virtual method;
2. so the output should be invoking the foo based on the type of pointer (which is Base), then the output should be Base?


regards,
George

You are correct, I thought I included that.

The code should read

Expand|Select|Wrap|Line Numbers
  1. Base* pb  = reinterpret_cast<Base*> (p);
  2.  
This resolves the initial problem. But again as soon as you comment out the foo definition in final, you get the compiler error that ashitpro was indicating.

To get the
Final
Final
Final

we declare the base class foo as virtual:
Expand|Select|Wrap|Line Numbers
  1. class Base {
  2. public:
  3.     virtual int foo() {cout << "Base" << endl; return 0;}
  4. };
  5.  
AS per your request the intire program is here:
Expand|Select|Wrap|Line Numbers
  1. #include <iostream>
  2. #include <stdafx.h> 
  3.  
  4. using namespace std;
  5.  
  6. class Base {
  7. public:
  8.     virtual int foo() {cout << "Base" << endl; return 0;}
  9. };
  10.  
  11. class Derived1: public Base
  12. {
  13. public:
  14.     int foo() {cout << "Derived1" << endl;return 0;}
  15. };
  16.  
  17. class Derived2: public Base
  18. {
  19. public:
  20.      int foo() {cout << "Derived2" << endl;return 0;}
  21. };
  22.  
  23. class Final: public Derived1, public Derived2 {
  24. public:
  25.   int foo() {cout << "Final" << endl;return 0;}
  26. };
  27.  
  28. int main()
  29. {
  30.     Final f;
  31.     Final* p = &f;
  32.     //Base* pb = (p); //error C2594: 'initializing' : ambiguous conversions from 'Final *' to 'Base *'
  33.     Base* pb = reinterpret_cast<Base*> (p);
  34.     f.foo();
  35.     p->foo();
  36.     pb->foo();
  37.  
  38.     return 0;
  39. }
  40.  
Happy Coding
Feb 8 '08 #13
hdanw
61
George,

Go back and read everything I posted.

There is a very good c++ explination, and the answer to your question can be found there.

Good Luck,

Dan -
Mar 20 '08 #14

Sign in to post your reply or Sign up for a free account.

Similar topics

9
by: Mark Turney | last post by:
I was reading "Practical C++ Programming" yesterday, and it mentioned that the order of execution for post-increment and post-decrement operators was ambiguous. I had previously learned that a...
21
by: Paul Steckler | last post by:
Here's some code that's giving me differing results, depending on the compiler. typedef foo { int A,B; } FOO; int main() {
0
by: Greg | last post by:
I have a web project that references the Crystal 10 dlls which are in the GAC, and in that same solution I have libraries that also reference them the same way. However when compiling I get...
0
by: ciaran.mchale | last post by:
I used Google to find information about JAXB 2.0 and I ended up downloading a document called "The Java Architecture for XML Binding (JAXB) 2.0: Proposed Final Draft September 30, 2005". ...
3
by: subramanian100in | last post by:
I have tried the following in VC++ 2005 Express Edition and g++ in Linux. Consider the class class my_complex { public: my_complex(double r, double i = 10.0) : re(r), im(i) { }...
2
by: subramanian100in | last post by:
Consider the program #include <iostream> using namespace std; void fn(const char * str, char x = 'Y') { cout << "from fn(const char *, char) - " << str << endl; return;
3
by: Adam Nielsen | last post by:
Hi everyone, I've run into yet another quirk with templates, which IMHO is a somewhat limiting feature of the language. It seems that if you inherit multiple classes, and those classes have...
10
by: subramanian100in | last post by:
consider the following program: #include <iostream> using namespace std; class my_complex { public: friend ostream & operator<<(ostream &os, const my_complex &c);
4
by: abendstund | last post by:
Hi, I have the following code and trouble with ambiguity due to operator overloading.. The code is also at http://paste.nn-d.de/441 snip>>
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.