Why Do Multiple-Inherited Functions With Same Name But Different Signatures Not Get Treated as Overloaded Functions

Why do multiple-inherited functions with same name but different signatures not get treated as overloaded functions?

Member lookup rules are defined in Section 10.2/2

The following steps define the result of name lookup in a class scope, C. First, every declaration for the name in the class and in each of its base class sub-objects is considered. A member name f in one sub-object B hides a member name f in a sub-object A if A is a base class sub-object of B. Any declarations that are so hidden are eliminated from consideration. Each of these declarations that was introduced by a using-declaration is considered to be from each sub-object of C that is of the type containing the declara-tion designated by the using-declaration. If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed. Otherwise that set is the result of the lookup.

class A {
public:
int f(int);

};
class B {
public:
int f();

};
class C : public A, public B {};
int main()
{
C c;
c.f(); // ambiguous
}

So you can use the using declarations A::f and B::f to resolve that ambiguity

class C : public A, public B {
using A::f;
using B::f;

};

int main()
{
C c;
c.f(); // fine
}

The second code works flawlessly because void foo(float) is inside C's scope. Actually d.foo(5); calls void foo(float) and not the int version.

Why is multiply inheriting a method with different signatures ambiguous?

Overloading occurs between names defined in the same scope. When multiple bases define the same name, the definitions are in different scopes, so they don't overload. If you add two using declarations to Derived you can pull the two names into Derived and they will then overload.

Function with same name but different signature in derived class

Functions in derived classes which don't override functions in base classes but which have the same name will hide other functions of the same name in the base class.

It is generally considered bad practice to have have functions in derived classes which have the same name as functions in the bass class which aren't intended to override the base class functions as what you are seeing is not usually desirable behaviour. It is usually preferable to give different functions different names.

If you need to call the base function you will need to scope the call by using A::foo(s). Note that this would also disable any virtual function mechanism for A::foo(string) at the same time.

Why can't GCC disambiguate multiple inherited functions (yet clang can)?

I think it has something to do with hiding the function(s) in the base classes, and the GCC's error message doesn't seem to help much, even if you use struct instead of enum : In fact, the error message is misleading, because now Ea and Eb are two different classes, with no implicit conversion from Ea to Eb, the ambiguity shouldn't arise, but GCC seems to disagree with me : http://ideone.com/cvzLW (see the modification also).

Anyway, if you bring the functions in the class scope, explicitly by writing using as:

struct Sbroken : Sa, Sb 
{
using Sa::operator();
using Sb::operator();
};

then it works : http://ideone.com/LBZgC

Same with other example as well:

struct Sbroken : Sa, Sb 
{
using Sa::f;
using Sb::f;
};

Code : http://ideone.com/3hojd

Overload resolution with multiple inheritance

Yes, this is expected behaviour.

Before overload resolution, name lookup is performed, which has its own set of rules. It has to find an unambiguous name and there isn't one in your case.

Ambiguous when two superclasses have a member function with the same name, but different signatures

The first part is due to member name lookup, that's why it fails.

I would refer you to: 10.2/2 Member name lookup

The following steps define the result of name lookup in a class scope,
C. First, every declaration for the name in the class and in each of
its base class sub-objects is considered. A member name f in one
sub-object B hides a member name f in a sub-object A if A is a base
class sub-object of B. Any declarations that are so hidden are
eliminated from consideration. Each of these declarations that was
introduced by a using-declaration is considered to be from each
sub-object of C that is of the type containing the declaration
designated by the using-declaration.

If the resulting set of declarations are not all from sub-objects of
the same type, or the set has a nonstatic member and includes members
from distinct sub-objects, there is an ambiguity and the program is
ill-formed
. Otherwise that set is the result of the lookup.

Now, for the matter with template functions.

As per 13.3.1/7 Candidate functions and argument list

In each case where a candidate is a function template, candidate
function template specializations are generated using template
argument deduction (14.8.3, 14.8.2). Those candidates are then handled
as candidate functions in the usual way. A given name can refer to one
or more function templates and also to a set of overloaded
non-template functions. In such a case, the candidate functions
generated from each function template are combined with the set of
non-template candidate functions.

And if you continue reading 13.3.3/1 Best viable function

F1 is considered to be a better function, if:

F1 is a non-template function and F2 is a function template
specialization

That's why the following snippet compiles and runs the non-template function without error:

D c;
c.f(1);

ambiguous error : C++11 use variadic template multiple inheritance

Before overload resolution, name lookup is performed, which has its own set of rules. It has to find an unambiguous name and there isn't one in your case.

You may use the following instead (https://ideone.com/ChvQ4q)

template<typename... Ts>
struct _EventsHandler;

template<typename T>
struct _EventsHandler<T> : T
{
using T::pushEvent;
};

template<typename T, typename... Ts>
struct _EventsHandler<T, Ts...> : T, _EventsHandler<Ts...>
{
using T::pushEvent;
using _EventsHandler<Ts...>::pushEvent;
};


Related Topics



Leave a reply



Submit