Почему разрешены избыточные квалификаторы имени класса?

Я наткнулся на такой код:

struct A {
    A() {}
    A(int) {}
};

struct B : A {
    void init(int i);
};

void B::init(int i) {
    A::A(i); // what is this?
}

int main() {
    B b;
    b.init(2);
}

Это скомпилировано и запущено с использованием бета-версии VC11 без ошибок или предупреждений с /W4.

Очевидное намерение состоит в том, чтобы вызвать B ::init для повторной инициализации базового подобъекта B A. Я считаю, что на самом деле он анализируется как объявление переменной для новой переменной с именем iи типом A. Компиляция с clang производит диагностику:

ConsoleApplication1.cpp:11:14: warning: declaration shadows a local variable
        A::A(i);
             ^
ConsoleApplication1.cpp:10:22: note: previous declaration is here
    void B::init(int i) {
                     ^
ConsoleApplication1.cpp:11:14: error: redefinition of 'i' with a different type
        A::A(i);
             ^
ConsoleApplication1.cpp:10:22: note: previous definition is here
    void B::init(int i) {
                     ^

Кажется любопытным, что на этот тип можно ссылаться с избыточной квалификацией класса.

Кроме того, A::A(i)по-разному анализируется VS11 и clang/gcc. Если я сделаю A::A(b), clang и gcc создадут переменную bтипа A, используя конструктор по умолчанию. VS11 выдает ошибку, говоря, что b— неизвестный идентификатор. VS11, по-видимому, анализирует A::A(i)как создание временного Aс использованием конструктора A::A(int)с iв качестве параметра. Когда избыточный квалификатор удален, VS анализирует источник как объявление переменной, как это делают clang и gcc, и выдает аналогичную ошибку о затенении переменной i.

Эта разница в синтаксическом анализе объясняет, почему VS11 задыхается от более чем одного дополнительного квалификатора; A::A::A::A(i)и почему, учитывая, что clang и gcc могут принимать один дополнительный квалификатор, любое число, превышающее одно дополнительное, дает тот же результат, что и одно дополнительное.

Вот еще один пример с избыточными квалификаторами в другом контексте. Кажется, что все компиляторы разбирают это как временную конструкцию:

class Foo {};

void bar(Foo const &) {}

int main() {
    bar(Foo::Foo());
}
  1. Почему вообще допускаются избыточные квалификаторы?
  2. Есть некоторые контексты, в которых можно ссылаться на конструкторы, например, синтаксис наследования конструкторов (class D : B { using B::B; };). но VS, похоже, разрешает это где угодно.Ошибается ли VS и правы ли clang и gcc в том, как анализируются избыточные квалификаторы?
  3. Я знаю, что VS все еще немного отстает с точки зрения соответствия стандартам, но меня немного удивляет, что современные, активно разрабатываемые компиляторы могут быть такими разными, в данном случае разрешая избыточный квалификатор как имя конструктора (. даже несмотря на то, что конструкторы не имеют имен )по сравнению с разрешением избыточных квалификаторов просто в тип, в результате чего VS создает временный объект, в котором другие объявляют переменную. Это может быть еще хуже, когда B b(A::A(i));анализируется clang и gcc как самый неприятный анализ, но VS видит это как объявление переменной bтипа Bс инициализатором. Много ли еще столь серьезных различий?
  4. Очевидно, что в переносимом коде следует избегать избыточных квалификаторов. Есть ли хороший способ предотвратить использование этой конструкции?
10
задан bames53 11 July 2012 в 16:33
поделиться