Объявление и инициализация переменной в условном операторе или операторе Control в C ++

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

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

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

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

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

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

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

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


person Daniel Bingham    schedule 04.10.2009    source источник
comment
Я не знаю, какой компилятор поддерживает это, но я лично использую этот подход каждый раз, когда делаю что-то, что не требует этой переменной, кроме использования ее в качестве временной. Тот же вопрос, является ли это плохой практикой?   -  person Chezz    schedule 04.10.2009
comment
Другой пример: stackoverflow.com/questions/1380135/   -  person Johannes Schaub - litb    schedule 05.10.2009
comment
Я бы сказал, что сама идея определения переменных по блокам - плохая практика, потому что она оправдывает длинные методы. Я рекомендую радикальные утверждения Боба Мартина относительно длины функции в его чрезвычайно полезной книге Чистый код; в частности, в любом случае метод действительно не должен иметь более одной управляющей структуры.   -  person Keith Pinson    schedule 11.01.2013


Ответы (9)


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

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

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


Редактировать:

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

Поэтому вместо того, чтобы использовать это:

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;
}
person RedGlyph    schedule 04.10.2009
comment
Во втором примере не используйте i в качестве переменной. Для чего-либо, кроме строго локальной переменной, сделайте имя значимым. - person David Thornley; 05.10.2009

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

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

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

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

person EFraim    schedule 04.10.2009

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

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

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

person Bill    schedule 04.10.2009
comment
Мне нравится, что. (Теперь с 15+ символами) - person jmucchiello; 05.10.2009

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

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

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

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

person sp2danny    schedule 17.02.2017

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

Проблема, похоже, заключается в круглых скобках вокруг объявления int. Он должен работать, если вы можете выразить задание и протестировать без них, т.е.

if (int i = read(socket)) {

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

person Ferruccio    schedule 04.10.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 является плохой идеей, независимо от того, что вы могли бы подумать об объявлении переменной там.

person Clifford    schedule 04.10.2009
comment
Эээ, не могли бы вы таким образом поместить результат read(socket) < 0 в переменную i? - person RedGlyph; 04.10.2009
comment
Точно; это то, что я сказал - это меняет смысл. Дело в том, что он компилируется, объявление переменной в условном выражении поддерживается, но не в середине выражения. Скобки помещают его в середину выражения. Что действительно, так это объявление с выражением инициализации. - person Clifford; 04.10.2009
comment
По крайней мере, одна группа пользователей прочитает ваши первые две строчки, решит, что вы ошибаетесь, и проголосует против. Возможно, стоит изменить первую строку, чтобы она подчеркивала, что вы не предполагаете, что это решит проблему. - person Richard Corden; 05.10.2009
comment
Я не собираюсь терять сон из-за нескольких голосов StackOverflow, если они не будут обменены на Air Miles! ;) Но точка взята. - person Clifford; 05.10.2009

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

if(int x = read(socket)) //x != 0
{
  if(x < 0) //handle error
  {}
  else //do work
  {}
}
else //x == 0  
{
  return true;
}
person White_Pawn    schedule 11.11.2009
comment
Мне это нравится, я не думал переставлять свои операторы if таким образом :) - person Daniel Bingham; 11.11.2009

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

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

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

person Jim Monte    schedule 03.04.2017