Я наткнулся на такой код:
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());
}
class D : B { using B::B; };
). но VS, похоже, разрешает это где угодно.Ошибается ли VS и правы ли clang и gcc в том, как анализируются избыточные квалификаторы?B b(A::A(i));
анализируется clang и gcc как самый неприятный анализ, но VS видит это как объявление переменной b
типа B
с инициализатором. Много ли еще столь серьезных различий?