#include <iostream>
using namespace std;
int main() {
int i = 0;
if (true) {
int i = 5;
cout << i << '\n';
}
return 0;
}
Пытался запустить приведенный выше код на Ideone, чтобы узнать, законно ли это. Результаты меня озадачивают:
5
, как ожидалось (1)Как вы можете видеть из моих ссылок, этот же код ведет себя радикально по-разному каждый раз, когда он компилируется на Ideone! Это пахнет неопределенным поведением (UB).
OK C++ известен своим неинтуитивным поведением, НО! - Я признаю, что это всего лишь моя интуиция, но я бы не ожидал, что даже C++ сделает переопределение переменной во внутренней области UB! Ожидается либо затенение, либо обязательная ошибка компиляции.
Действительно ли мой код соответствует стандарту C++ UB или это просто особенность Ideone и / или gcc? Если это UB, то это UB, потому что я повторно объявил i
или по какой-то другой причине, которую я сейчас не замечаю?
Это совершенно верно. Два i
- это полностью разные переменные. Я раздвоил ваш код (1)
, и он работал без изменений, без ошибок.
Не специфично для вопроса, но убегайте от онлайн-компиляторов, которые выдают «ошибку компиляции» в качестве единственного вывода. Напоминает мне о моих первых днях, когда я изучал основы, и лучшее, что вы могли получить, было «Ошибка 5 в строке 25» - но даже это дало бы вам хотя бы строчку.
@SergeyA, обычно Ideone выдает ошибки компилятора, см., Например, ideone.com/UQzO2A. На этот раз этого не произошло, что еще больше меня озадачивает.
Разве Godbolt не позволяет делать что-то подобное, а также выбирает, какой компилятор использовать? Кажется, это лучший вариант для такого теста
В любом случае, лучше включить предупреждения: wandbox.org/permlink/aJwp2JOZtvrQcil4
@TimRandall godbolt не запускает код, а только компилирует его (и показывает сборку). Wandbox позволяет запускать его и видеть результат (и поддерживает выбор из десятков версий GCC или Clang для компиляции). Coliru также позволяет вам запускать его и выбирать из GCC или Clang. Все они полезны по-разному.
Заголовок должен гласить: "Идеоне ужасный сайт?"
@JonathanWakely, спасибо за информацию. Я никогда не задумывался о важности увидеть результат в этом случае. Я просто предположил, что компилятор выдаст предупреждение или ошибку (или нет, если он настроен как слабый). Мне и в голову не пришло ожидать от кода UB.
Потому что это не УБ. Но тест включает cout << i << '\n';
, и поэтому проверка вывода, по-видимому, имеет значение в этом случае. Просто жаль, что ideone, кажется, сломан и так запутал OP.
Is it UB to redeclare a variable within enclosed scope?
Нет это не так.
Ошибка компилятора, которую вы видите, скорее всего, вызвана тем, что внешний i
объявлен, но не используется.
В противном случае ваш код в порядке.
У меня он отлично работает на https://ideone.com/AwVJqZ, а также на моем рабочем столе.
Тогда почему каждый раз при компиляции он ведет себя по-разному? И почему ошибка компилятора кажется недетерминированной?
@gaazkam Потому что IDEOne - мусор? Я бы предложил использовать Wandbox или Coliru
Объявление, но не использованное, не является ошибкой, если только кто-то не компилирует с переключателем, который говорит, что предупреждения следует рассматривать как ошибки. В этом случае компилятор не соответствует определению языка, и все ставки отключены.
@JonathanWakely: соответствие не требует, чтобы компилятор мог эффективно обрабатывать какую-либо конкретную программу, пока существует некоторая (возможно, надуманная и бесполезная) программа, которая номинально выполняет ограничения на перевод в N1570 5.2.4.1, которые он будет обрабатывать в соответствии с стандарт. Качественные реализации не должны отклонять программы без какой-либо причины, но Стандарт не занимается вопросами качества реализации.
@supercat, да, а? [intro.compliance] p2 является соответствующей ссылкой. Я не сказал, что соответствие означает принятие и правильное выполнение каждой программы. Дело в том, что -Werror=unused-variable
делает GCC несоответствующим, потому что он не может компилировать программы, которые не содержат нарушений правил (невыполнение требования [intro.compliance] p2 bullet 2.1). Он не отклоняет программу из-за ограничений ресурсов, он отклоняет ее, потому что -Werror=unused-variable
явно запрашивает режим несоответствия. Я отвечал на удаленный комментарий Натана, в котором неверно говорилось об обратном.
Нет неопределенного поведения, стандарт разрешает имя сокрытие, это описано в [basic.scope.hiding]:
A declaration of a name in a nested declarative region hides a declaration of the same name in an enclosing declarative region; see [basic.scope.declarative] and [basic.lookup.unqual].
и [basic.scope.declarative] говорит:
Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity. In general, each particular name is valid only within some possibly discontiguous portion of program text called its scope. To determine the scope of a declaration, it is sometimes convenient to refer to the potential scope of a declaration. The scope of a declaration is the same as its potential scope unless the potential scope contains another declaration of the same name. In that case, the potential scope of the declaration in the inner (contained) declarative region is excluded from the scope of the declaration in the outer (containing) declarative region.
и приводит следующий пример:
[ Example: In
int j = 24; int main() { int i = j, j; j = 42; }
the identifier j is declared twice as a name (and used twice). The declarative region of the first j includes the entire example. The potential scope of the first j begins immediately after that j and extends to the end of the program, but its (actual) scope excludes the text between the , and the }. The declarative region of the second declaration of j (the j immediately before the semicolon) includes all the text between { and }, but its potential scope excludes the declaration of i. The scope of the second declaration of j is the same as its potential scope. — end example ]
Почему вы видите такие переменные результаты от IDEone, я не знаю. В нем нет большого количества ручек для понимания того, что происходит. Wandbox - одна из нескольких альтернатив, которые предоставляют множество регуляторов и не демонстрируют такой же изменчивости для этого случая.
В вашем коде нет UB. Это просто заставляет меня еще больше избегать IDEOne. Жаль, что он не говорит, в чем ошибка компилятора.