Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a program rejected as ambiguous that could be resolved by overload resolution?

The following program is rejected by gcc as ambiguous:

struct Aint 
{
    virtual void foo(int);
};

struct Astring 
{
    virtual void foo(std::string);
};

struct A: public Aint, public Astring {};

int main()
{
  std::string s;

  A a;
  a.foo(s);

  return 0; 
}

> vt.cpp: In function ‘int main()’: vt.cpp:13:9: error: request for
> member ‘foo’ is ambiguous
>        a.foo(s);
>          ^ vt.cpp:5:34: note: candidates are: virtual void Astring::foo(std::__cxx11::string)
>      struct Astring {virtual void foo(std::string);};
>                                   ^ vt.cpp:4:31: note:                 virtual void Aint::foo(int)
>      struct Aint {virtual void foo(int);};

Clang consistently rejects the program for the same reason:

clang -std=c++1y -c vt.cpp 

vt.cpp:13:9: error: member 'foo' found in multiple base classes of different types
      a.foo(s);
        ^
vt.cpp:4:31: note: member found by ambiguous name lookup
    struct Aint {virtual void foo(int);};
                              ^
vt.cpp:5:34: note: member found by ambiguous name lookup
    struct Astring {virtual void foo(std::string);};

I am not completely sure if I understood the lookup rules in section 10.2 correctly, so I am going through the rules in the following steps to compute the lookup set S(foo, A):

1. A does not contain `foo`, so rule 5 applies and S(foo, A) is initially empty. We need to calculate the lookup sets S(foo, Aint) and S(foo, Afloat) and merge them to S(foo, A) = {}
2. S(foo, Aint) = {Aint::foo}
3. S(foo, Afloat) = {Afloat::foo}
4. Merge S(foo, Aint) = {Aint::foo} into S(foo, A) = {} to get S(foo, A) = {Aint::foo} (second case of 6.1)
5. Merge S(foo, Afloat) = {Afloat::foo} into {Aint::foo}. This create an ambiguous lookup set because of rule 6.2

The result set is an invalid set and thus the program is ill-formed.

I am wondering why the program is being rejected so early. It should be easy for the compiler to do overload resolution in this case because both functions have identical names but different signatures, so there is no real ambiguity. Is there a technical reason that this is not done, or are there other reasons which would accept incorrect programs? Does anybody know the rational behind the decision to reject these programs so early?

like image 467
Jens Avatar asked Dec 22 '15 14:12

Jens


1 Answers

In C++, there is no overloading across scopes – derived class scopes are not an exception to this general rule .Please refer for more detail. Anyway your example can be improved by specifying that you want to use both version of foo by "using" keyword. See below example.

Example program:

#include <iostream>
#include <string>

struct Aint 
{
     void foo(int){std::cout<<"\n Aint";}
};

struct Astring 
{
     void foo(std::string){std::cout<<"\n Astring";}
};

struct A: public Aint, public Astring {
    using Aint::foo;
    using Astring::foo;
    };

int main()
{
  std::string s;

  A a;
 a.foo(s);

  return 0; 
}
output:Astring
like image 160
SACHIN GOYAL Avatar answered Nov 15 '22 12:11

SACHIN GOYAL