Почему программа отклоняется как неоднозначная, которую можно разрешить путем разрешения перегрузки?

Следующая программа отвергается gcc как неоднозначная:

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 постоянно отклоняет программу по одной и той же причине:

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);};

Я не совсем уверен, правильно ли я понял правила поиска в разделе 10.2, поэтому я просматриваю правила в следующих шагах, чтобы вычислить набор поиска 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

Результирующий набор является недопустимым набором, и поэтому программа имеет неверный формат.

Мне интересно, почему программа отвергается так рано. В этом случае компилятору должно быть легко выполнить разрешение перегрузки, потому что обе функции имеют одинаковые имена, но разные сигнатуры, поэтому реальной двусмысленности нет. Есть ли техническая причина, по которой это не делается, или есть другие причины, которые могут принять неправильные программы? Кто-нибудь знает причину столь раннего отказа от этих программ?


person Jens    schedule 22.12.2015    source источник
comment
вы пробовали это на clang? кажется компилятор ошибается   -  person Angelus Mortis    schedule 22.12.2015
comment
@AngelusMortis Я добавил вывод из clang 3.6. Я думаю, что это правильно в соответствии с правилами поиска в стандарте.   -  person Jens    schedule 22.12.2015
comment
Просто воспроизвел те же результаты, это не компилятор.   -  person Ziezi    schedule 22.12.2015
comment
MS Visual Studio 2013 тоже не скомпилирует, хотя сообщение об ошибке менее полное: Error 4 error C3861: 'foo': identifier not found s:\epsitec\testco\testco.cpp 21 1 testco Error 3 error C2385: ambiguous access of 'foo' s:\epsitec\testco\testco.cpp 21 1 testco   -  person Jabberwocky    schedule 22.12.2015
comment
Может ли это быть связано с тем, что string не инициализирован?   -  person Ziezi    schedule 22.12.2015
comment
@simplicisveritatis, нет, если вы замените std_string, например, на char*, результат будет таким же.   -  person Jabberwocky    schedule 22.12.2015
comment
Это правильное поведение. Цитируя Бьерна, в C++ нет перегрузки между областями действия.   -  person T.C.    schedule 22.12.2015
comment
@Т.С. но в классе A нет функции foo -> тогда поиск имени должен попытаться найти foo в базовых классах-> находит две функции с именем foo, следовательно, возникает ошибка неоднозначности? , что означает, что разрешение перегрузки находится в работе.   -  person Angelus Mortis    schedule 22.12.2015
comment
если поставить using Astring; раньше, станет еще интереснее   -  person Ziezi    schedule 22.12.2015
comment
@T.C.Спасибо за ссылку. Мне нужно найти приведенные там ссылки (D&E и TC++PL3), но это также должно ответить на мой вопрос.   -  person Jens    schedule 22.12.2015
comment
мой мозг сейчас болит :(   -  person Angelus Mortis    schedule 22.12.2015


Ответы (1)


В C++ нет перегрузки между областями действия — области производных классов не являются исключением из этого правила. общее правило. Подробнее см. . В любом случае ваш пример можно улучшить, указав, что вы хотите использовать обе версии foo с помощью ключевого слова «using». См. пример ниже.

Пример программы:

#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
person SACHIN GOYAL    schedule 22.12.2015
comment
В исходном примере были вариативные шаблоны. Что-то вроде template<typename T> struct A {virtual void foo(T);}; template<typename... T> struct B: public A<T>... { /* using A<T>...; */ };. В этом примере (это не мой код, я видел его на SO) вы не можете написать объявление using, потому что оно не работает с расширением пакета параметров шаблона (см. groups.google.com/a/isocpp.org/forum/?fromgroups#!topic/). - person Jens; 22.12.2015
comment
@Jens: я понял вашу точку зрения. Не могли бы вы опубликовать полный исходный пример для лучшего понимания - person SACHIN GOYAL; 22.12.2015
comment
Спасибо Йенс. Я посмотрю :) - person SACHIN GOYAL; 22.12.2015