Это потому, что поиск имени останавливается, если он находит имя в одной из ваших баз. Это не будет смотреться дальше в других базах. Функция в B shadows функция в A. Вы должны повторно объявить функцию A в области видимости B, чтобы обе функции были видны изнутри B и C:
class A
{
public:
void foo(string s){};
};
class B : public A
{
public:
int foo(int i){};
using A::foo;
};
class C : public B
{
public:
void bar()
{
string s;
foo(s);
}
};
Редактировать: Реальное описание, которое дает Стандарт: (от 10.2 / 2):
Следующие шаги определяют результат поиска имени в области видимости класса C. Сначала каждое объявление для
имя в классе и в каждом из его базовых классов подобъектов. Имя члена f в одном
объект B скрывает имя члена f в подобъекте A, если A является подобъектом базового класса объекта B. Любые объявления
которые так скрыты, исключаются из рассмотрения. Каждая из этих деклараций, представленная
using-декларация считается от каждого подобъекта C, который имеет тип, содержащий декларацию
ция, обозначенная использованием-декларации.96) Если результирующий набор объявлений не все из подобъектов
того же типа, или набор имеет нестатический член и включает в себя элементы из различных подобъектов, есть
двусмысленность и программа плохо сформирована. В противном случае этот набор является результатом поиска.
В другом месте (чуть выше) сказано следующее:
Для id-выражения [ что-то вроде "foo" ] поиск имени начинается в области видимости этого класса; для квалифицированного идентификатора [ что-то вроде «A :: foo», A является спецификатором вложенного имени ], поиск имени начинается в области спецификатора вложенного имени. Поиск имени происходит перед контролем доступа (3.4, пункт 11).
([...] поставлено мной). Обратите внимание, что это означает, что даже если ваш foo в B является приватным, foo в A все равно не будет найден (потому что контроль доступа происходит позже).