Элегантный способ для "if (T t =...) {} else вернуть t;"?

Есть ли лучший способ для этой "идиомы"?

if(State s = loadSomething()) { } else return s;

Другими словами, я хочу что-то сделать, что может вернуть ошибку (с сообщением) или состояние успеха, и если была ошибка, я хочу ее вернуть. Это может стать очень повторяющимся, поэтому я хочу сократить его. Например

if(State s = loadFoobar(&loadPointer, &results)) { } else return s;
if(State s = loadBaz(&loadPointer, &results)) { } else return s;
if(State s = loadBuz(&loadPointer, &results)) { } else return s;

Это не должно использоваться исключения, которые я бы предпочел иначе (неподходящий для этой сборки). Я мог бы написать небольшой класс BooleanNegator<State>, который сохраняет значение и отрицает его логическую оценку. Но я хочу избежать этого ad-hoc и предпочитаю решение для повышения/стандартизации.

Ответ 1

Вы можете сделать:

for (State s = loadSomething(); !s; ) return s;

но я не уверен, что он более изящный, и он определенно менее читабель...

Ответ 2

Я предполагаю, что контекст похож на

State SomeFunction()
{
     if(State s = loadSomething()) { } else return s;
     return do_something_else();
}

без исключения исключений, если do_something_else() делает что-то актуальное для SomeFunction() и возвращает State. В любом случае, результат продолжения внутри функции должен привести к возврату State, поскольку падение с конца приведет к тому, что вызывающий объект будет демонстрировать поведение undefined.

В этом случае я бы просто реструктурировал функцию

State SomeFunction()
{
     if (State s = loadSomething())
        return do_something_else();
     else
        return s;
}

Неявные предположения заключаются в том, что State имеет некоторый оператор (например, operator bool()), который может быть проверен, что копирование a State возможно (подразумевается наличием a loadSomething(), которое возвращает его) и относительно недорогим, и что два экземпляра State могут существовать за один раз.

Ответ 3

Помимо некоторых умных/хакерских видов использования разных ключевых слов для получения того же поведения или добавления дополнительных или дополнительных сложных шаблонов или макросов для получения ключевого слова unless() или для того, чтобы каким-то образом выполнить команду !, я ' d придерживайтесь только основных вещей.

Это одно из мест, где я (возможно) добавлю лишние "лишние" скобки:

void someFunction()
{
    // some other code

    { State s = loadSomething(); if(!s) return s; }

    // some other code
}

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

void someFunction()
{
    // some other code

    {
        State s = loadSomething();
        if(!s)
            return s;
    }

    // some other code
}

Это может выглядеть как увеличение области s, но на самом деле это эквивалентно объявлению State s в if(). Все благодаря дополнительным скобкам, которые явно ограничивают видимость локальных s.

Однако некоторые люди просто "ненавидят", видя { .. } не связанными с ключевым словом/классом/функцией/и т.д., или даже считают его нечитаемым из-за "предполагая, что ключевое слово if/while/etc было случайно удалено".

Еще одна идея пришла ко мне после добавления повторяющегося примера. Вы могли бы попробовать трюк, известный из языков сценариев, где && и || могут возвращать значения non-bool:

State s = loadFoobar(&loadPointer, &results);
s = s || loadBaz(&loadPointer, &results);
s = s || loadBuz(&loadPointer, &results);
if(!s) return s;

однако есть проблема: в отличие от языков script, в С++ такие перегрузки && и || теряют свою короткозамкнутую семантику, что делает эта попытка бессмысленна.

Однако, поскольку dyp указал на очевидную вещь, как только область s будет увеличена, теперь можно ввести простой if. Его видимость может быть снова ограничена дополнительными {}:

{
    State s;
    if(!(s = loadFoobar(&loadPointer, &results))) return s;
    if(!(s = loadBaz(&loadPointer, &results))) return s;
    if(!(s = loadBuz(&loadPointer, &results))) return s;
}