Объявление и инициализация переменной в инструкции Conditional или Control в С++
В Stroustrup Язык программирования С++: Special Edition (3-е изд.), Stroustrup пишет, что объявление и инициализация переменных в условных выражениях управляющих операторов не только разрешены, но и поощряются. Он пишет, что поощряет его, потому что он уменьшает объем переменных только до области, для которой они необходимы. Так что-то вроде этого...
if ((int i = read(socket)) < 0) {
// handle error
}
else if (i > 0) {
// handle input
}
else {
return true;
}
... хороший стиль программирования и практика. Переменная i
существует только для блока операторов if
, для которых это необходимо, и затем выходит за рамки.
Однако эта функция языка программирования, похоже, не поддерживается g++ (версия 4.3.3 Ubuntu specific compile), что удивительно для меня. Возможно, я просто вызываю g++ с флагом, который отключает его (флагов, которые я назвал, это -g
и -Wall
). Моя версия g++ возвращает следующую компиляцию при компиляции с этими флагами:
socket.cpp:130: error: expected primary-expression before ‘int’
socket.cpp:130: error: expected `)' before ‘int’
В ходе дальнейшего исследования я обнаружил, что я, похоже, не единственный, у кого есть компилятор, который этого не поддерживает. И, похоже, возникла некоторая путаница в этом вопросе относительно того, какой синтаксис предположительно был стандартным в языке и какие компиляторы с ним компилируются.
Итак, вопрос в том, какие компиляторы поддерживают эту функцию и какие флаги должны быть установлены для ее компиляции? Это вопрос того, чтобы быть в определенных стандартах, а не в других?
Кроме того, просто из любопытства, люди вообще согласны с Stroustrup, что это хороший стиль? Или это ситуация, когда создатель языка получает в голове идею, которая не обязательно поддерживается языковым сообществом?
Ответы
Ответ 1
Разрешено объявлять переменную в управляющей части вложенного блока, но в случае if
и while
переменная должна быть инициализирована на числовое или логическое значение, которое будет интерпретироваться как условие, Он не может быть включен в более сложное выражение!
В конкретном случае, который вы показываете, похоже, вы не можете найти способ выполнить к сожалению.
Я лично считаю хорошей практикой держать локальные переменные как можно ближе к их действительной продолжительности жизни в коде, даже если это звучит шокирующим при переключении с C на С++ или с Pascal на С++ - мы привыкли видеть все переменные в одном месте. С некоторой привычкой вы находите ее более читаемой, и вам не нужно искать в другом месте, чтобы найти объявление. Кроме того, вы знаете, что он не используется до этого момента.
Edit:
Сказав это, я не считаю хорошей практикой смешать слишком много в одном заявлении, и я думаю, что это общее мнение. Если вы влияете на значение переменной, то используйте ее в другом выражении, код будет более читабельным и менее запутанным, разделив обе части.
Поэтому вместо использования этого:
int i;
if((i = read(socket)) < 0) {
// handle error
}
else if(i > 0) {
// handle input
}
else {
return true;
}
Я бы предпочел, чтобы:
int i = read(socket);
if(i < 0) {
// handle error
}
else if(i > 0) {
// handle input
}
else {
return true;
}
Ответ 2
Я считаю это хорошим стилем при использовании с возможным указателем NULL:
if(CObj* p = GetOptionalValue()) {
//Do something with p
}
Таким образом, объявлен ли p, это действительный указатель. Отсутствие опасности обмана с помощью указателя.
С другой стороны, по крайней мере, в VС++ это единственное поддерживаемое использование (т.е. проверка правильности назначения)
Ответ 3
Я использую const как можно больше в этих ситуациях. Вместо вашего примера я бы сделал:
const int readResult = read(socket);
if(readResult < 0) {
// handle error
}
else if(readResult > 0)
{
// handle input
}
else {
return true;
}
Итак, хотя область охвата не содержится, это не имеет большого значения, так как переменная не может быть изменена.
Ответ 4
Я столкнулся с аналогичной проблемой :
Проблема заключается в круглых скобках вокруг объявления int
. Он должен работать, если вы можете выразить назначение и тест без них, т.е.
if (int i = read(socket)) {
должен работать, но это означает, что тест != 0
, который не является тем, что вы хотите.
Ответ 5
Пока вы можете использовать объявление как логическое выражение, вы не можете разместить объявление в середине выражения. Я не могу не думать о том, что вы неправильно читаете то, что говорит Бьярн.
Этот метод полезен и желательно в основном для контрольных переменных для циклов, но в этом случае я считаю, что он не рекомендуется и не обеспечивает ясности. И, конечно, это не сработает!;)
if( <type> <identifier> = <initialiser> ) // valid, but not that useful IMO
if( (<type> <identifier> = <initialiser>) <operator> <operand> ) // not valid
for( <type> <identifier> = <initialiser>;
<expression>;
<expression> ) // valid and desirable
В вашем примере вы вызвали функцию с побочными эффектами в условном выражении, что ИМО - плохая идея, независимо от того, что вы могли бы подумать о объявлении этой переменной.
Ответ 6
Добавление к тому, что сказал RedGlyph и Ferruccio.
Возможно, мы можем сделать следующее, чтобы все еще объявить в условном выражении ограничение его использования:
if(int x = read(socket)) //x != 0
{
if(x < 0) //handle error
{}
else //do work
{}
}
else //x == 0
{
return true;
}
Ответ 7
Они фиксируют это в С++ 17:
if (int i = read(socket); i < 0)
где if
может иметь инструкцию инициализации.
см. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0305r0.html
Ответ 8
В дополнение к хорошим ответам других людей вы всегда можете ограничить область действия фигурными скобками:
{
const int readResult = read(socket);
if(readResult < 0) {
// handle error
}
else if(readResult > 0)
{
// handle input
}
else {
return true;
}
}
Ответ 9
Не имея прямого отношения к вопросу, все примеры сначала обрабатывают ошибки. Поскольку существует 3 случая ( > 0 → данные, == 0 → соединение закрыто и < 0 → ошибка), это означает, что наиболее распространенный случай получения новых данных требует двух тестов. Сначала проверка нa > 0 сократит ожидаемое количество тестов почти на половину. К сожалению, подход if (int x = read (socket)), заданный White_Pawn, по-прежнему требует 2 тестов для случая данных, но предложение С++ 17 может быть использовано для тестирования сначалa > 0.