Должна ли декларация использования скрывать унаследованную виртуальную функцию?

struct level0
{
  virtual void foo() = 0;
};

struct level1 : level0
{
  virtual void foo() { cout <<" level1  " << endl; }
};

struct level2 : level1
{
  virtual void foo() { cout <<" level2 " << endl; }
};

struct level3 : level2
{
  using level1::foo;
};

int main()
{
  level1* l1 = new level3;
  l1->foo();
  level3 l3;
  l3.foo();
  return 0;
}

приведенный выше код с использованием gcc дает

level2
level1

но в icc дает

 level2
 level2

Какой из них правильный или он не определен стандартом?

Изменить: это доказывает, что есть ошибка, рассмотрим следующую основную функцию

int main()
{
    level3 l3;
    l3.foo();               // This prints level1

    level3* pl3 = &l3;
    pl3->foo();             // This prints level2

    level3& rl3 = l3;
    rl3.foo();              // This prints level1

    level3& rpl3 = *pl3;
    rpl3.foo();             // This prints level2

    return 0;
}

Таким образом, один и тот же объект при прямом использовании дает разные результаты, а при использовании через указатель одного и того же типа дает разные результаты!!!


person balki    schedule 12.01.2011    source источник
comment
Я бы предположил, что GCC правильный, но я не уверен.   -  person Billy ONeal    schedule 12.01.2011
comment
Я подтвердил поведение с TDM MinGW g++ 4.4.1. Это ошибка g++, пожалуйста, сообщите об этом. Ура и чт.,   -  person Cheers and hth. - Alf    schedule 12.01.2011
comment
Забавно, я только вчера изучал это.   -  person aschepler    schedule 12.01.2011
comment
Это было исправлено в GCC 4.8.   -  person Potatoswatter    schedule 30.01.2013


Ответы (3)


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

Это известная ошибка g++.

Как вы заметили, при вызове функции-члена через ссылку или указатель, а не в случае, когда известен динамический тип, бага не происходит.

person aschepler    schedule 12.01.2011
comment
+1 за стандартную цитату, что касается ошибки: сообщалось в 2004 году, последнее обновление в 2006 году --> Я думаю, никого это не волнует, учитывая простоту обхода :) - person Matthieu M.; 12.01.2011

using level1::foo; представляет функцию foo в классе level3, которая ссылается на level1::foo.

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

Однако, поскольку level1::foo является виртуальным, я предполагаю, что, вызывая его, вы должны вызывать level2::foo, поэтому icc должен быть прав.

Во всяком случае, я не уверен.

person peoro    schedule 12.01.2011

Способ получить level1 level1, конечно, будет:

struct level3 : level2
{
   virtual void foo() { level1::foo(); }
};

Ваша директива «using», похоже, информирует компилятор о том, что если у вас есть уровень 3 и вы вызываете для него foo, он должен вызвать версию уровня 1, но не перезаписывает ее в v-таблицу.

gcc выглядит неправильно из-за несоответствия, не уверен насчет icc, потому что не знаю, что указывает стандарт.

person CashCow    schedule 12.01.2011