Есть ли идиома как `if (Value * value = getValue())`, когда вы введете ответ на выражение полученного значения?

Я часто использую общий

if (Value * value = getValue())
{
    // do something with value
}
else
{
    // handle lack of value
}

Теперь я также часто делаю

QString error = someFunctionReturningAnErrorString(arg);
if (!error.isEmpty())
{
     // handle the error
}
// empty error means: no error

Что все отлично, но я бы хотел, чтобы переменная error была привязана к if -блоку. Есть ли приятная идиома для этого? Очевидно, я могу просто обернуть всю часть внутри другого блока.

Это, очевидно, не работает:

if(QString error = someFunctionReturningAnErrorString(arg), !error.isEmpty())
{
    // handle the error
}
// empty error means: no error

И, к сожалению (но по уважительным причинам) QString не может быть преобразован в bool, так что это тоже не работает:

if(QString error = someFunctionReturningAnErrorString(arg))
{
    // handle the error
}
// empty error means: no error

Любые предложения?

Ответ 1

Единственный способ использовать эту идиому, сохраняя при этом код понятным, - это то, что ваша функция возвращает объект, который конвертируется в bool таким образом, что true указывает, что вы хотите взять ветку и false означает, что вы не заботятся об этом. Все остальное просто приведет к созданию только кода для записи.

Одним из таких объектов, который может быть релевантным, является boost::optional. Дано:

boost::optional<QString> someFunctionReturningAnErrorString(T arg);

Вы можете использовать идиому, которую хотите, естественным образом:

if (auto error = someFunctionReturningAnErrorString(arg)) {
    // ...
}

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

Ответ 2

Нет. Нет такой идиомы, и нет такого синтаксиса, как это!

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

Просто напишите его, как сейчас.

Если вы действительно не хотите утечки области, введите новую область:

{
   const QString error = someFunctionReturningAnErrorString(arg);
   if (!error.isEmpty()) {
      // handle the error
   }
}
// The above-declared `error` doesn't exist down here

Я использую эту схему довольно много, хотя меня довольно обвиняют в пристрастии к сфере охвата, поэтому сделайте так, как хотите.

Ответ 3

В принципе нет чистого способа сделать это.

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

struct ErrorString
{
    ErrorString(QString&& s) : s{move(s)} {}
    operator bool() {return !s.isEmpty();}

    QString s;
};

И тогда вы можете написать:

if(ErrorString error = someFunctionReturningAnErrorString(arg))
{
    // handle the error
}
// empty error means: no error

Но я не очень люблю это решение.

Ответ 4

Вы можете использовать:

for(QString error = someFunctionReturningAnErrorString(arg); !error.isEmpty(); /* too bad 'break' is invalid here */)
{
    // handle the error
    break;
}

но это уродливо, и ваш код трудно читать. Пожалуйста, не делайте этого.

Ответ 5

if(auto message = maybe_filter( getError(arg), [](auto&&str){
  return !str.isEmpty();
}) {
}

где maybe_filter берет T и тестовую функцию и возвращает optional<T>. optional<T> пуст, если evalutating тестовая функция на T дает false и T в противном случае.

Или, действительно, измените вашу ошибку, получив API для возврата необязательной строки.

Ответ 6

Вы можете использовать лямбда.

auto error_string_handler = [](QString && error) {
    if (error.isEmpty()) return;
    //...
}

error_string_handler(someFunctionReturningAnErrorString(arg));