Инициализация 'unused' пропускается 'goto label' - почему я получаю его для std::string, а не для int?
Я столкнулся с этой ошибкой в некотором коде, и после некоторых экспериментов я наткнулся на эту странность - я получаю ее за std::string
, но не для int
,
За std::string
я получил error C2362: initialization of 'unused' is skipped by 'goto label'
:
{ goto label;
std::string unused;
label:;
}
За int
Я не получаю никакой ошибки, однако:
{ goto label;
int unused = 10;
label:;
}
Почему разница? Это потому что std::string
есть нетривиальный деструктор?
2 ответа
Это описано в разделе проекта стандарта C++. 6.7
Заявление декларации, которое говорит (выделение мое):
Можно передавать в блок, но не так, чтобы обойти объявления с инициализацией. Программа, которая переходит на87 из точки, в которой переменная с автоматическим хранением находится вне области действия, до точки, где она находится в области видимости, имеет неправильную форму, если переменная не имеет скалярного типа, типа класса с тривиальным конструктором по умолчанию и тривиальным деструктором, cv-квалифицированная версия одного из этих типов или массив одного из предыдущих типов и объявляется без инициализатора (8.5).
и предоставляет следующий пример:
void f() {
// ...
goto lx; // ill-formed: jump into scope of a
ly:
X a = 1;
// ...
lx:
goto ly; // OK, jump implies destructor
// call for a followed by construction
// again immediately following label ly
}
Хотя в обоих случаях должна появиться ошибка, поскольку в обоих случаях вы пропускаете инициализацию, это, однако, было бы хорошо:
goto label;
int unused ;
label:
Так Visual Studio
здесь не правильно, оба gcc
а также clang
генерировать и ошибка для этого кода, gcc
говорит:
error: crosses initialization of 'int unused'
int unused = 10;
^
Конечно Visual Studio
может иметь такое расширение, если оно документирует его, но оно не является переносимым для использования такого расширения, как я указал clang
а также gcc
сгенерировать ошибку для этого.
Мы можем найти обоснование того, почему мы не хотим перепрыгивать через инициализацию в отчете о дефектах 467, в котором стремились добавить такое же ограничение для локальной статической переменной (оно было отклонено):
[...] автоматические переменные, если они не инициализированы явно, могут иметь неопределенные ("мусорные") значения, включая представления ловушек, [...]
Ошибка компилятора. Оба незаконны. Что не является незаконным, однако:
{
goto label;
int unused;
unused = 10;
label:
;
}
И то и другое std::string unused;
а также int unused = 10;
иметь инициализаторы (конструктор по умолчанию в случаеstd::string
), и вам нельзя прыгать по определению с помощью инициализатора. Прыжки вокруг одного без инициализатора, вероятно, позволят избежать взлома кода вроде
switch ( something )
{
int i;
case 0:
i = x;
// ...
break;
case 1:
i = y;
// ...
break;
// ...
}
Я бы не назвал этот хороший код, но я бы не удивился, обнаружив его в более старом C, а C++ старается не ломать подобные вещи.