Переопределение переменной в цикле for в переполнении стека

При попытке скомпилировать следующий (упрощенный) код для нескольких платформ я обнаружил, что он не работает на некоторых, а именно на IBM xlC_r. Дальнейшее расследование показало, что оно также не работает на комо и лязге. Он успешно компилируется с g ++ и CC Solaris.

Вот код:

int main()
{
int a1[1];
bool a2[1];

for (int *it = a1, *end = a1+1; it != end; ++it) {
//...
bool *jt = a2, *end = a2+1;
//...
}
}

Ошибка xlC_r:

"main.cpp", line 8.25: 1540-0400 (S) "end" has a conflicting declaration.
"main.cpp", line 6.25: 1540-0425 (I) "end" is defined on line 6 of "main.cpp".

лягушка ошибка:

main.cpp:8:25: error: redefinition of 'end' with a different type
bool *jt = a2, *end = a2+1;
^
main.cpp:6:25: note: previous definition is here
for (int *it = a1, *end = a1+1; it != end; ++it) {
^

ошибка комо:

"ComeauTest.c", line 8: error: "end", declared in for-loop initialization, may not
be redeclared in this scope
bool *jt = a2, *end = a2+1;
^

Вопрос в том, почему это ошибка?

Просматривая стандарт 2003, он говорит следующее (6.5.3):

The for statement
for ( for-init-statement; condition; expression ) statement
is equivalent to
{
for-init-statement;
while ( condition ) {
statement;
expression;
}
}
except that names declared in the for-init-statement are in the same
declarative-region as those declared in condition

Здесь нет имен, объявленных в состоянии.

Далее сказано (6.5.1):

When the condition of a while statement is a declaration, the scope
of the variable that is declared extends from its point of declaration
(3.3.1) to the end of the while statement. A while statement of the form
while (T t = x) statement
is equivalent to
label:
{
T t = x;
if (t) {
statement;
goto label;
}
}

Опять же, я не уверен, что это актуально, поскольку в условии нет декларации. Поэтому, учитывая эквивалентную перезапись из 6.5.3, мой код должен быть таким же, как:

int main()
{
int a1[1];
bool a2[1];

{
int *it = a1, *end = a1+1;
while (it != end) {
//...
bool *jt = a2, *end = a2+1;
//...
++it;
}
}
}

Что, очевидно, позволило бы вновь объявить конец.

14

Решение

Стандарт несколько двусмысленный. Код, который вы цитируете как эквивалент while цикл подразумевает, что существует внутренняя область видимости, где объявления внутри цикла могут скрывать объявления в условии; однако в стандарте также говорится (цитируя C ++ 11, поскольку у меня нет C ++ 03 под рукой):

6.4 / 2 Правила условий применяются как к операторам выбора, так и к for а также while заявления

6.4 / 3 Если имя повторно объявлено в самом внешнем блоке подсостояния, контролируемого условием, то объявление, которое повторно объявляет имя, является некорректным.

6.5.3 / 1 имена, объявленные в выражении for-init-, находятся в том же декларативном регионе, что и имена, объявленные в условии

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

Старые (до 1998 года) версии языка помещали объявления в операторе for-init в декларативную область вне цикла. Это означало, что ваш код был бы действительным, но это не так:

for (int i = ...; ...; ...) {...}
for (int i = ...; ...; ...) {...}  // error: redeclaration of i
7

Другие решения

Я думаю, что код правильный. ИМО, проблема с брекетами. Обратите внимание, что оператор for определяется как:

for (for-init-Statement; условие; выражение)

Тело цикла не имеет фигурных скобок, они добавляются при использовании составного оператора. Но составной оператор добавляет свой собственный декларативный регион, поэтому внутренняя декларация не должна конфликтовать с for-init-statement,

Следующий код компилируется нормально с clang и G ++ (обратите внимание на двойные скобки):

for (int *it = a1, *end = a1+1; it != end; ++it) {{
//...
bool *jt = a2, *end = a2+1;
//...
}}

Я предполагаю, что компилятор clang пытается оптимизировать, как если бы цикл был определен как:

for (for-init-Statement; условие; выражение) { Заявление-сл }

С постепенным изменением значения: оба декларативных региона слиты воедино.

На втором, хотя, даже без скобок используются:

for (int x=0; ;)
char x;

Должен правильно скомпилироваться. Из C ++ черновик 6.5, пар. 2:

Подстановка в выражении итерации неявно определяет область видимости блока.

Итак char x; сам по себе определяет (неявно) область видимости блока, и конфликтующие объявления не должны возникать.

3

Текущая версия стандарта ясна на этом:

6.5. Итерационные операторы [stmt.iter]

2 — подстанция в итерация-заявление [например, for loop] неявно определяет область действия блока (3.3), которая вводится и выходит каждый раз через цикл.

C имеет аналогичное правило:

6.8.5 Итерационные операторы

Семантика

5 — оператор итерации — это блок, область действия которого является строгим подмножеством области действия его
ограждающий блок. Тело цикла также является блоком, область действия которого является строгим подмножеством области действия.
итерационного оператора.

0

Некоторые, как правило, более старые компиляторы делают переменные, объявленные в циклах for, видимыми вне области цикла.

Чтобы заставить все компиляторы вести себя более новым (и лучшим) способом, объявите макрос следующим образом:

// In older compilers, variables declared in a for loop statement
// are in the scope of the code level right outside the for loop.
// Newer compilers very sensibly limit the scope to inside the
// loop only. For compilers which don't do this, we can spoof it
// with this macro:
#ifdef FOR_LOOP_VARS_NEED_LOCAL_SCOPE
#define for if(0); else for
#endif

Затем для каждого компилятора с более старым поведением определите FOR_LOOP_VARS_NEED_LOCAL_SCOPE. Например, вот как вы могли бы сделать это для MSVC < 8:

#ifdef _MSC_VER
#if _MSC_VER < 1400   //  earlier than MSVC8
#define FOR_LOOP_VARS_NEED_LOCAL_SCOPE
#endif
#endif
0

Я немного опоздал на вечеринку здесь, но я думаю, что это наиболее недопустимо в этом отрывке из стандарта C ++ 11:

3.3.3 Область действия блока [basic.scope.local]

4 — Имена, объявленные в заявлении for-init, объявлении for-range, и в состоянии, если, в то время как, для,
и операторы switch являются локальными по отношению к операторам if, while, for или switch (включая
заявление), и не должен быть повторно объявлен в последующем условии этого заявления, ни в самой внешней
блок (или, для оператора if, любой из самых внешних блоков) контролируемого оператора
; см. 6.4.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector