Подтвердить что ты не робот

Объявление и инициализация переменной в инструкции 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, что это хороший стиль? Или это ситуация, когда создатель языка получает в голове идею, которая не обязательно поддерживается языковым сообществом?

4b9b3361

Ответ 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;
}

Ответ 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.