优雅的"if(T t = ...) { } else return t;"方式?

Elegant way for "if(T t = ...) { } else return t;"?

本文关键字:else return 方式 if      更新时间:2023-10-16

是否有更好的方法来实现这个"习语"?

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>来存储这个值,并对它的布尔值求反。但是我希望避免这样做,而更喜欢一个增强/标准解决方案。

你可以这样做:

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

但是我不确定它是否更优雅,而且它肯定不太可读…

我假设上下文类似于

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

不抛出异常,其中do_something_else()做一些与SomeFunction()相关的事情并返回State。无论哪种方式,在函数内继续的结果都需要导致返回State,因为从末尾掉下来将导致调用者表现出未定义的行为。

在这种情况下,我将简单地将函数重构为

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

隐含的假设是State有一些可以测试的运算符(例如operator bool()),复制State是可能的(通过存在返回一个操作符的loadSomething()暗示)并且相对便宜,并且两个State实例可以同时存在。

除了一些智能/黑客使用不同的关键字来获得相同的行为,或添加或多或少复杂的额外模板或宏来获得unless()关键字或以某种方式设法注入!操作符,我坚持只做基本的事情。

这是我(可能)注入额外的"不必要的"括号的地方之一:

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的作用域,但实际上相当于在if()中声明了State s。这都要感谢额外的括号,它显式地限制了本地s的可见性。

然而,有些人只是"讨厌"看到{ .. }没有与关键字/类/函数等结合,甚至认为它是不可读的,因为"暗示if/while/etc关键字被意外删除"。

在你添加了重复的例子之后,我又有了一个想法。您可以尝试脚本语言中已知的技巧,其中&&||可能返回非bool值:

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

然而,有一个问题:与脚本语言相比,在c++中,&&||的重载失去了它们的短路语义,这使得这种尝试毫无意义。

然而,正如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;
}