Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: Daniel BinghamОтветов: 9

В книге Страуструпа Язык программирования 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’

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

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

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

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

c++declarationcompiler-constructionconditional

Ответы

Ответ 1 · рейтинг 18 · RedGlyph

Разрешено объявить переменную в управляющей части вложенного блока, но в случае 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;
}

Ответ 2 · рейтинг 12 · EFraim

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

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

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

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

Ответ 3 · рейтинг 6 · Bill

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

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

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

Ответ 4 · рейтинг 6 · sp2danny

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

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

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

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

Ответ 5 · рейтинг 4 · Ferruccio

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

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

if (int i = read(socket)) {

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

Ответ 6 · рейтинг 3 · Clifford

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

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

Ответ 7 · рейтинг 3 · White_Pawn

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

if(int x = read(socket)) //x != 0
{
  if(x < 0) //handle error
  {}
  else //do work
  {}
}
else //x == 0
{
  return true;
}

Ответ 8 · ChronoTrigger

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

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

Ответ 9 · Jim Monte

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

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы