Hey guys,

I was wondering, with this code:

main.cpp

#include <iostream>
using namespace std;

class car {
public:
    car (float speed) :
            speed(speed) {}

    car () :
            speed(0) {}

    void cruise(float speed) {
        this->speed = speed;
        cout << "New speed: " << getSpeed() << endl;
    }

    void brake(float power) {
        this->speed -= power*power/4;
    }

    float getSpeed() {
        return speed;
    }

private:
    float speed;
};

class racer : public car {
public:
    void boost(float power) {
        cout << "BOOST! ";
        cruise(getSpeed() + power*power/3);
    }
};

class tank : public car {
public:
    bool shoot(float aimTime) {
        cout << "Shot ";
        if (aimTime > 5.0) {
            cout << "hits!" << endl;
            return true; //hit!
        } else {
            cout << "misses!" << endl;
            return false; //miss!
        }
    }

};

class racetank : public racer, public tank {
public:
    bool boostShoot(float power, float aimTime) {
        boost(power*2);
        return shoot(aimTime*2);
    }
};

int main() {
    racetank mycar;
    mycar.car::cruise(50);
    mycar.boost(20);
    mycar.car::brake(5);
    mycar.boostShoot(35, 1.4);

    return 0;
}

MinGW's GCC gives me these errors:

D:\MyDocs\Code\InheritThis\main.cpp||In function `int main()':|
D:\MyDocs\Code\InheritThis\main.cpp|62|error: `car' is an ambiguous base of `racetank'|
D:\MyDocs\Code\InheritThis\main.cpp|64|error: `car' is an ambiguous base of `racetank'|

How come the compiler still complains about ambiguity?

It knows the function is not overloaded (?)
It knows the function is not virtual.

Can't the compiler be sure that when I call the function like that, it's the same function?

Thanks in advance,

PS:
I know the solution to this is to use virtual inheritance, but I was wondering why C++ works like it does.

NicAx64 commented: hmm nice and smart bug , I think I find something new +1

Recommended Answers

All 7 Replies

Replying to myself yeey.

Let a class in memory be resembled by:

[classname]

Class inheritance in memory looks like this, according to wiki.

class a;
class b : a;

class a looks like this:
[a]

b looks like this:
[a]

So if we'd have a class base and c, we can create a diamond inheritance:

class base;
class a : base;
class b : base;
class c : a, b;

Their memory-pictures are like this:

base: [base]
a: [base][a]
b: [base]
c: [base][a][base][c]

c, more specific, looks like this:
[a::base][a][b::base][c]

So, naturally, a::base and b::base data members can differ, but a::base::func() must be the same as b::base::func() given that a and b don't overload and func() is not virtual right? How else can they be different?

> How else can they be different?
Yep, they must be the same function.

It works if you pick a specific copy like this: mycar.racer::cruise(50); or this mycar.tank::cruise(50); But "diamond inheritance" looks like something to avoid in general. Seems strange that the language wasn't designed to include only one copy in such a case.

Quoting myself:
I know the solution to this is to use virtual inheritance, but I was wondering why C++ works like it does.

The correct solution is to virtual inherit the class cars in race and tank class. This way, the compiler creates only one car inside of racetank, and 2 vtables to be able to still call the functions.

Basically, I was just wondering why GCC doesn't just pick one of the functions and give me a warning instead of 2 errors.

--Edit, a bit later:

So the compiler asks me what this pointer to give to the function, that of class a or b? It doesn't matter if one only uses class c, but when one degrades class c to class a, b or base, the data members may have different values then what was expected if the compiler chose instead of the programmer. Say function modifies int value, how would the programmer know what value it was modified, that of a or b? Therefore a name must be presented if one wants to call functions like this.

The "proper" solution, in most cases, is to go by virtual inheritance.

This was helpful research. Multiple Virtual Inheritance seems a nice and powerful tool, albeit a bit tricky.

Basically, I was just wondering why GCC doesn't just pick one of the functions and give me a warning instead of 2 errors.

I have compiled your mentioned source by the microsoft C++ compiler too. the same error message is there.

error C2385: 'racetank::car' is ambiguous

So it's not a compiler bug or compiler specific thing , it should be something that clearly defined on the C++ language specification.

Let this thread open may be later some expertise see this thread and reply.

The correct solution is to virtual inherit the class cars in race and tank class. This way, the compiler creates only one car inside of racetank, and 2 vtables to be able to still call the functions.

and using virtual doesn't slove this problem. I used the Microsoft C++ compiler.I think it's not the reason.

yes there is a correct solution for this ,, see this ,
http://msdn.microsoft.com/en-us/library/42a01w2f(VS.71).aspx

anyway why this happen is not still clear. so please keep this
thread open.

#include <iostream>
using namespace std;

class car {
public:
    car (float speed) :
            speed(speed) {}

    car () :
            speed(0) {}

    void cruise(float speed) {
        this->speed = speed;
        cout << "New speed: " << getSpeed() << endl;
    }

    void brake(float power) {
        this->speed -= power*power/4;
    }

    float getSpeed() {
        return speed;
    }

private:
    float speed;
};

class racer : public car {
public:
    void boost(float power) {
        cout << "BOOST! ";
        cruise(getSpeed() + power*power/3);
    }
};

class tank : public car {
public:
    bool shoot(float aimTime) {
        cout << "Shot ";
        if (aimTime > 5.0) {
            cout << "hits!" << endl;
            return true; //hit!
        } else {
            cout << "misses!" << endl;
            return false; //miss!
        }
    }

};

class racetank : public racer, public tank {
public:
    
	using tank::cruise ;
	using racer::brake ;
	bool boostShoot(float power, float aimTime) {
        boost(power*2);
        return shoot(aimTime*2);
    }
};

int main() {


    racetank mycar;
    mycar.cruise(50);
    mycar.boost(20);
    mycar.brake(5);
    mycar.boostShoot(35, 1.4);

    return 0;
}

This code compiles nicely , so this is the solution , but the problem is
still unclear.

No, that's not the proper solution to this.

This is

#include <iostream>
#include <string>
using namespace std;

class car {
public:
    car (float speed) :
            speed(speed) {}

    car () :
            speed(0) {}

    void cruise(float speed) {
        this->speed = speed;
        getSpeed(true);
    }

    void brake(float power) {
        this->speed -= power*power/4;
        getSpeed(true);
    }

    float getSpeed(bool verbose = false) {
        if (verbose) {
            cout << "New speed: " << getSpeed() << endl;
        }
        return speed;
    }

private:
    float speed;
};

class racer : virtual public car {
public:
    void boost(float power) {
        cout << "BOOST! ";
        cruise(getSpeed() + power*power/3);
    }
};

class tank : virtual public car {
public:
    bool shoot(float aimTime) {
        cout << "Shot ";
        if (aimTime > 5.0) {
            cout << "hits!" << endl;
            return true; //hit!
        } else {
            cout << "misses!" << endl;
            return false; //miss!
        }
    }

};

class racetank : public racer, public tank {
public:
    bool boostShoot(float power, float aimTime) {
        boost(power*2);
        return shoot(aimTime*2);
    }
};

int main() {
    racetank mycar;
    mycar.cruise(50);
    mycar.boost(20);
    mycar.brake(5);
    mycar.boostShoot(35, 1.4);

    return 0;

    return 0;
}

This way, racetank only has one base object car, not two. Virtual Inheritance :D

Member Avatar for stephen.id

....

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.