É porque a pesquisa de nome para se encontrar um nome em uma de suas bases. Não vai olhar além em outras bases. A função em B obscurece a função em A. Você deve declarar novamente a função de A no escopo de B, de modo que ambas as funções sejam visíveis de dentro de B e 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);
}
};
Editar: A descrição real que o Padrão fornece é (de 10.2 / 2):
As etapas a seguir definem o resultado da pesquisa de nome em um escopo de classe, C. Primeiro, cada declaração para o nome na classe e em cada um de seus subobjetos de classe base é considerada. Um nome de membro f em um subobjeto B oculta um nome de membro f em um subobjeto A se A for um subobjeto de classe base de B. Quaisquer declarações que estejam tão ocultas são eliminadas da consideração. Cada uma dessas declarações que foi introduzida por uma declaração de uso é considerada como sendo de cada subobjeto de C que é do tipo que contém a declaração designada pela declaração de uso. 96) Se o conjunto de declarações resultante não for todos de subobjetos do mesmo tipo, ou o conjunto tem um membro não estático e inclui membros de subobjetos distintos, há uma ambigüidade e o programa está mal formado. Caso contrário, esse conjunto é o resultado da pesquisa.
Tem o seguinte a dizer em outro lugar (logo acima):
Para uma expressão de id [ algo como "foo" ], a pesquisa de nome começa no escopo de classe deste; para um id qualificado [ algo como "A :: foo", A é um especificador de nome aninhado ], a pesquisa de nome começa no escopo do especificador de nome aninhado. A pesquisa de nome ocorre antes do controle de acesso (3.4, cláusula 11).
([...] colocado por mim). Note que isso significa que mesmo se seu foo em B for privado, o foo em A ainda não será encontrado (porque o controle de acesso acontece mais tarde).