Очевидно, неоднозначный вызов не вызывает ошибку компиляции в GCC

Я был удивлен тем фактом, что GCC делает не рассмотреть призыв к foo() в следующей программе неоднозначно:

#include <iostream>

struct B1 { bool foo(bool) { return true; } };
struct B2 { bool foo(bool) { return false; } };

struct C : public B1, public B2
{
using B1::foo;
using B2::foo;
};

int main()
{
C c;

// Compiles and prints `true` on GCC 4.7.2 and GCC 4.8.0 (beta);
// does not compile on Clang 3.2 and ICC 13.0.1;
std::cout << std::boolalpha << c.foo(true);
}

Вышеупомянутый вызов функции компилируется и возвращается true на GCC 4.7.2 и GCC 4.8.0 (бета), в то время как не скомпилируется (как я и ожидал) на Clang 3.2 и ICC 13.0.1.

Это случай «Диагностика не требуется«или это ошибка в GCC? Ссылки на стандарт C ++ 11 приветствуются.

9

Решение

§7.3.3 / 3:

В объявлении использования, используемом в качестве объявления члена, спецификатор вложенного имени должен называть базовый класс определяемого класса. Если такое объявление-использование называет конструктор, спецификатор nested-name-name должен называть прямой базовый класс определяемого класса; иначе он вводит набор объявлений, найденных при поиске имени члена (10.2, 3.4.3.1).

¶14:

… [Примечание: два объявления-использования могут представлять функции с одинаковыми именами и одинаковыми типами параметров. Если для вызова неквалифицированного имени функции разрешение перегрузки функции выбирает функции, представленные такими объявлениями использования, вызов функции некорректен.

¶16:

В целях разрешения перегрузки функции, которые вводятся объявлением использования в
Производный класс будет обрабатываться так, как если бы он был членом производного класса.

Итак using декларации являются допустимыми, но функции являются одноранговыми в том же наборе перегрузки, как вы сказали, и программа некорректна.

4

Другие решения

Призыв к foo(true) в вашей программе, как вы говорите, явно неоднозначно; кроме того, он является неоднозначным в соответствии с алгоритмом, представленным в §10.2, и, следовательно, он должен быть помечен при использовании. (Пометка using декларация будет неправильной; 10.2 (1) четко заявляет, что неоднозначное использование имен помечается при поиске, а не при объявлении.)

Интересно сравнить эту программу с аналогичной, которая является предметом признанная ошибка gcc (немного изменен из отчета об ошибке, чтобы сделать параллель более понятной):

#include <iostream>

struct A {
static int foo() {return 1;}
static int foo(char) { return 2;}
};

struct B1 : A {
//   using A::foo;
};
struct B2 : A {
//   using A::foo;
};

struct C : B1, B2 {
//   using B1::foo;
//   using B2::foo;
};

int main()
{
std::cout << C::foo();
}

Вышеуказанная программа верна; несмотря на наследство алмазов, foo является статическим членом Aтак что это не неоднозначно. На самом деле, GCC компилирует его без проблем. Тем не менее, раскомментируя два случая using A::foo, который ничего не меняет ни foo, заставляет gcc выдавать странно дублированную ошибку, отмеченную в отчете об ошибке. Раскомментируя два using объявления внутри C, который предположительно вызывает другую ошибку, которая является предметом этого вопроса, затем маскирует static function ошибка и приводит к повторной компиляции программы.

Кажется, clang обрабатывает все возможные варианты этой программы, чего бы это ни стоило.

Наконец, обратите внимание, что явно объявленный foo(bool) в C (в оригинальной программе) победит любой foo(bool) принести в Cсфера действия using деклараций. Я подозреваю, что обе эти ошибки являются результатом плохой бухгалтерии при попытке отслеживать различные объявления функций в области видимости каждого класса и их индивидуальное происхождение (как последовательность using объявления и объявления функций).

1