Вызов метода С++ и неоднозначность разрешения области действия типа

Я надеюсь, что заголовок действительно описывает то, что я хотел спросить...

Я написал фрагмент кода, который компилируется с помощью gcc и работает так, как я задумал. Однако он не компилируется с помощью llvm, и код выполняется по-другому при компиляции с помощью icc!
Вот пример проблемы:

#include <iostream>

using std::cout; using std::endl;

class A {
public:
  virtual void foo() { cout << "A::foo()" << endl; }
};

class B : public A {
public:
  typedef A  base;
  virtual void foo() { cout << "B::foo()" << endl; }
};

int main() {
  typedef B  base;
  base* bp = new B();
  bp->base::foo(); 
}

вывод gcc: A::foo()
вывод icc: B::foo()

Может ли кто-нибудь объяснить, что стандарт говорит об этом случае?


person Nowakus    schedule 20.06.2012    source источник
comment
Я бы сказал, что это ошибка в GCC и ICC, так как B::base не является членом группы B, а это означает, что доступ к нему как к члену (bp->base) невозможен.   -  person Some programmer dude    schedule 20.06.2012
comment
Я согласен с @JoachimPileborg, более того, base можно интерпретировать как B в этой области. Вы скомпилировали с флагами предупреждений? (-Стена для gcc)   -  person Geoffroy    schedule 20.06.2012
comment
Разве это не неопределенное поведение, потому что main не имеет требуемой формы? Похоже, все компиляторы правы.   -  person Kerrek SB    schedule 20.06.2012
comment
@Наваз: ты уверен? Я никогда не мог понять правила поиска имени; Clang считает, что два base неоднозначны, и не предпочитает один другому. Я действительно хотел бы, чтобы у нас был авторитетный ответ здесь, 3 компилятора - 3 поведения ... это, конечно, странная штука.   -  person Matthieu M.    schedule 20.06.2012
comment
@KerrekSB: Хорошо, теперь он должен быть готов к использованию ... и все же вы получите проблему.   -  person Matthieu M.    schedule 20.06.2012
comment
Вы можете опубликовать комментарий на серия STL, посвященная поиску имен.   -  person Kerrek SB    schedule 20.06.2012
comment
@MatthieuM.: Хорошо, теперь это ошибка :-)   -  person Kerrek SB    schedule 20.06.2012
comment
Какую версию gcc вы используете? Вставленный код не компилируется во всех доступных мне версиях gcc (с 4.3.4 по 4.7.0). Возможно, вы скомпилировали какой-то другой код и просто показали его здесь?   -  person PlasmaHH    schedule 20.06.2012
comment
@PlasmaHH: Правда? Похоже, код компилируется и запускается на ideone, который использует gcc 4.3.4: ideone.com/daJbT. Это также работает, если вы включите c++11, используя gcc 4.5.1: ideone.com/KT1Jj.   -  person Luc Touraille    schedule 20.06.2012
comment
@LucTouraille: Если вы сравните редактирование сообщения с отметкой времени моего комментария, принимая во внимание, что его написание занимает некоторое время, вы заметите, что на момент написания был код, который не компилировался.   -  person PlasmaHH    schedule 20.06.2012
comment
@PlasmaHH: Хорошо, я думал, что ваш комментарий был ответом на правку Матье, извините за путаницу.   -  person Luc Touraille    schedule 20.06.2012
comment
Итак... кажется, что и в Clang, и в icc есть ошибка, и gcc все правильно понял. У кого-нибудь есть свежая копия Clang для тестирования на магистрали?   -  person Matthieu M.    schedule 20.06.2012


Ответы (2)


Из С++ 11, §3.4.5/4:

If the id-expression in a class member access is a qualified-id of the form
    class-name-or-namespace-name::...
the class-name-or-namespace-name following the . or -> operator is first looked up in the class of the object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire postfix-expression.

Я не думаю, что это может быть яснее. Это находит B::base, поэтому вывод должен быть A::foo().

person James Kanze    schedule 20.06.2012
comment
Спасибо за помощь, вы молодцы. Этот конкретный абзац действительно очень ясен и точен! - person Nowakus; 20.06.2012

Я думаю, что эта часть стандарта актуальна:

3.4.3.1 Члены класса [class.qual]

1) Если спецификатор вложенного имени квалифицированного идентификатора назначает класс, имя, указанное после спецификатора вложенного имени, просматривается в области действия класса (10.2), за исключением случаев, перечисленных ниже. Имя должно представлять одного или нескольких членов этого класса или одного из его базовых классов (раздел 10). [Примечание: на член класса можно ссылаться с использованием квалифицированного идентификатора в любой точке его потенциальной области (3.3.7). —конец примечания ] Исключениями из приведенного выше правила поиска имени являются следующие:

- имя деструктора ищется, как указано в 3.4.3;

- идентификатор типа преобразования идентификатора функции преобразования просматривается таким же образом, как идентификатор типа преобразования в доступе к члену класса (см. 3.4.5);

— имена в аргументе-шаблоне идентификатора-шаблона просматриваются в контексте, в котором встречается все постфиксное-выражение.

- поиск имени, указанного в объявлении-использования (7.3.3), также находит имена классов или перечислений, скрытые в той же области видимости (3.3.10).

base:: в этом случае, похоже, «назначает» класс, поэтому поиск выполняется в рамках класса. Я не понимаю, как может применяться какой-либо из исключений, поэтому это область действия класса, поскольку base эквивалентно A.

(5.1.1-8 указывает, что в этом случае это квалифицированный идентификатор и что применяется 3.4.3.1)

person PlasmaHH    schedule 20.06.2012
comment
Согласовано. Мне это тоже кажется вполне ясным. Код правильный, хорошо определенный, и вывод должен быть A::foo. - person James Kanze; 20.06.2012