Igor Semenov
Igor Semenov

Reputation: 483

Ambigulty while calling overloaded operator

Here is the sample code:

struct A
{
    virtual int operator & ( A & ) { return 0; }
};

struct B : public A {};
struct C : public A {};

struct D : public C, public B {};

int main()
{
    D d;
    std::cout << &d << std::endl;
    return 0;
}

It perfectly works in VS 2008, but GCC fails to compile it:

../src/TestCast.cpp: In function ‘int main()’:
../src/TestCast.cpp:26:16: error: request for member ‘operator&’ is ambiguous
../src/TestCast.cpp:15:14: error: candidates are: virtual int A::operator&(A&)
../src/TestCast.cpp:15:14: error:                 virtual int A::operator&(A&)
make: *** [src/TestCast.o] Error 1

As far as I see, it looks for operator& overload by name, not by signature, so it find ambiguous overload and produces an error.

The question is: is it correct by Standard? If not, which paragraph describes it? Is there any way to make GCC accept this code (I mean, lookup by signature, not by name).

BTW, I know, how to fix this code. I just want to know, WHY an error appears.

Upvotes: 2

Views: 98

Answers (3)

Saksham
Saksham

Reputation: 9380

What do are achieving by this code is called diamond-inheritance problem. Explaining it briefly, during compilation, the compiler finds the operator & ambiguous as it cannot make out whether to call the B class inherited version or the C class inherited version.

To overcome that, declare your class definition as

struct B : virtual public A {};

Which makes only one copy of your function available in class D.

Upvotes: 0

Igor Semenov
Igor Semenov

Reputation: 483

What I've found in Standard:

3.4 Name lookup [basic.lookup]

1 The name lookup rules apply uniformly to all names (including typedef-names (7.1.3), namespace-names (7.3) and class-names (9.1)) wherever the grammar allows such names in the context discussed by a particular rule. Name lookup associates the use of a name with a declaration (3.1) of that name. Name lookup shall find an unambiguous declaration for the name (see 10.2). Name lookup may associate more than one declaration with a name if it finds the name to be a function name; the declarations are said to form a set of overloaded functions (13.1). Overload resolution (13.3) takes place after name lookup has succeeded. The access rules (clause 11) are considered only once name lookup and function overload resolution (if applicable) have succeeded. Only after name lookup, function overload resolution (if applicable) and access checking have succeeded are the attributes introduced by the name’s declaration used further in expression processing (clause 5).

Upvotes: 0

Nemanja Boric
Nemanja Boric

Reputation: 22157

What you caused is a diamond-inheritance issue, and you may solve it with virtual inheritance.

In A you have declared virtual operator& which is also defined in both B and C. Now, both these methods are defined inside D as you're using multiple inheritance.

From standard (10.1 Multiple base classes).

A class shall not be specified as a direct base class of a derived class more than once. [ Note: A class can be an indirect base class more than once and can be a direct and an indirect base class. There are limited things that can be done with such a class. The non-static data members and member functions of the direct base class cannot be referred to in the scope of the derived class. However, the static members, enumerations and types can be unambiguously referred to. — end note ] [Example:

and

A base class specifier that does not contain the keyword virtual, specifies a non-virtual base class. A base class specifier that contains the keyword virtual, specifies a virtual base class. For each distinct occurrence of a non-virtual base class in the class lattice of the most derived class, the most derived object (1.8) shall contain a corresponding distinct base class subobject of that type. For each distinct base class that is specified virtual, the most derived object shall contain a single base class subobject of that type. [Example: for an object of class type C, each distinct occurrence of a (non-virtual) base class L in the class lattice of C corresponds one-to-one with a distinct L subobject within the object of type C. Given the class C defined above, an object of class C will have two subobjects of class L as shown below.

L       L
|       |
A       B
  \   /
    C

Figure 3 — Non-virtual base 5 In such lattices, explicit qualification can be used to specify which subobject is meant. The body of function C::f could refer to the member next of each L subobject: void C::f() { A::next = B::next; } // well-formed Without the A:: or B:: qualifiers, the definition of C::f above would be ill-formed because of ambiguity (10.2).

Upvotes: 4

Related Questions