Объявление и инициализация переменной в операторе Conditional или Control в C ++ - PullRequest
25 голосов
/ 04 октября 2009

В Stroustrup's Язык программирования C ++: Special Edition (3rd Ed) , Страуструп пишет, что объявление и инициализация переменных в условных выражениях управляющих операторов не только разрешены, но и приветствуются. Он пишет, что поощряет это, потому что это уменьшает область действия переменных до той области, для которой они необходимы. Так что-то вроде этого ...

if ((int i = read(socket)) < 0) {
    // handle error
}
else if (i > 0) {
    // handle input
}
else {
    return true;
}

... это хороший стиль программирования и практика. Переменная i существует только для блока операторов if, для которого она требуется, и затем выходит из области видимости.

Однако эта функция языка программирования, похоже, не поддерживается g ++ (компиляция, специфичная для Ubuntu версии 4.3.3), что меня удивляет. Возможно, я просто вызываю g ++ с флагом, который отключает его (флаги, которые я назвал, это -g и -Wall). Моя версия g ++ возвращает следующую ошибку компиляции при компиляции с этими флагами:

socket.cpp:130: error: expected primary-expression before ‘int’
socket.cpp:130: error: expected `)' before ‘int’

В ходе дальнейших исследований я обнаружил, что, похоже, я был не единственным с компилятором, который не поддерживает это. И, похоже, в этом вопросе была некоторая путаница относительно того, какой именно синтаксис был якобы стандартным в языке и какие компиляторы компилируют его.

Итак, вопрос в том, какие компиляторы поддерживают эту функцию и какие флаги необходимо установить для ее компиляции? Это проблема того, чтобы быть в определенных стандартах, а не в других?

Кроме того, просто из любопытства согласны ли люди со Страуструпом, что это хороший стиль? Или это ситуация, когда у создателя языка возникает идея, которая не обязательно поддерживается языковым сообществом?

Ответы [ 9 ]

18 голосов
/ 04 октября 2009

Разрешено объявлять переменную в управляющей части вложенного блока, но в случае if и while переменная должна быть инициализирована числовым или логическим значением, которое будет интерпретировано как условие , Это нельзя включить в более сложное выражение!

В конкретном случае, который вы показываете, к сожалению, вы не можете найти способ выполнить это.

Лично я считаю хорошей практикой сохранять локальные переменные как можно ближе к их фактическому времени жизни в коде, даже если это звучит шокирующе, когда вы переключаетесь с C на C ++ или с Pascal на C ++ - мы привыкли видеть переменные в одном месте. С некоторой привычкой, вы находите его более читабельным, и вам не нужно искать в другом месте, чтобы найти объявление. Более того, вы знаете, что до этого момента он не использовался.


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;
}
11 голосов
/ 04 октября 2009

Я считаю, что это хороший стиль, когда используется возможно с указателем NULL:

if(CObj* p = GetOptionalValue()) {
   //Do something with p
}

Таким образом, объявлено ли p, это действительный указатель. Нет опасности доступа к висячему указателю.

С другой стороны, по крайней мере в VC ++ поддерживается только одно использование (т. Е. Проверяется, верно ли присвоение)

6 голосов
/ 05 октября 2009

В этих ситуациях я использую как можно больше const. Вместо твоего примера я бы сделал:

const int readResult = read(socket);
if(readResult < 0) {
    // handle error
} 
else if(readResult > 0)
{
    // handle input
} 
else {
    return true;
} 

Таким образом, хотя область действия не содержится, это не имеет значения, поскольку переменная не может быть изменена.

5 голосов
/ 17 февраля 2017

Они исправляют это в c ++ 17:

if (int i = read(socket); i < 0)

, где if может иметь оператор инициализатора.

см. http://www.open -std.org / jtc1 / sc22 / wg21 / docs / documents / 2016 / p0305r0.html

4 голосов
/ 04 октября 2009

Я столкнулся с подобной проблемой :

Кажется, проблема в скобках вокруг объявления int. Это должно работать, если вы можете выразить задание и протестировать без них, т. Е.

if (int i = read(socket)) {

должно работать, но это означает, что тест равен != 0, а это не то, что вам нужно.

3 голосов
/ 11 ноября 2009

Добавление к тому, что сказали Редглиф и Ферруччо. Может быть, мы можем сделать следующее, чтобы по-прежнему объявлять в условном выражении ограничение его использования:

if(int x = read(socket)) //x != 0
{
  if(x < 0) //handle error
  {}
  else //do work
  {}
}
else //x == 0  
{
  return true;
}
3 голосов
/ 04 октября 2009

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

Техника полезна и желательна в основном для управляющих переменных для циклов for, но в этом случае я считаю, что это неуместный и не служит ясности. И конечно это не работает! ;)

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

В вашем примере вы вызвали функцию с побочными эффектами в условном выражении, что является плохой идеей IMO, независимо от того, что вы можете подумать об объявлении переменной там.

0 голосов
/ 03 апреля 2017

Хотя это и не связано напрямую с вопросом, во всех примерах на первом месте стоит обработка ошибок. Поскольку существует 3 случая (> 0 -> данные, == 0 -> соединение закрыто и <0 -> ошибка), это означает, что наиболее распространенный случай получения новых данных требует двух тестов. Проверка на> 0 сначала сократила бы ожидаемое количество тестов почти вдвое. К сожалению, подход «if (int x = read (socket))», предоставленный White_Pawn, по-прежнему требует 2 теста для случая данных, но предложение C ++ 17 может быть использовано для проверки сначала на> 0.

0 голосов
/ 17 февраля 2017

Чтобы дополнить хорошие ответы других людей, вы всегда можете ограничить область действия переменной скобками:

{    
  const int readResult = read(socket);
  if(readResult < 0) {
    // handle error
  } 
  else if(readResult > 0)
  {
    // handle input
  } 
  else {
    return true;
  } 
}
...