有没有更优雅的方法来使用带有独立 if() 的 else 语句

Is there a more elegant way to use one else statement with independent if()s

我目前正在使用 C++ 的 SFML 并尝试调整 window 的大小,但是我发现我的问题的解决方案不太合适,我正在寻找是否有更好的方法。

我有一个对象可以多次调整大小,所以它可能适合 window 或不适合,如果 window 较大,我必须扩展它,如果它较小,我必须缩小它。但是,我有 window 的最小尺寸,如果对象适合我想将 window 重置为该尺寸。

这是我现在使用的伪代码:

if (Object.getSize().x > window.getSize().x || Object.getSize().y > window.getSize.y){
    if(Object.getSize().x > windowMinSize.x){
        window.resize(Object.getSize().x, window.getSize().y)
    }
    if(Object.getSize().y > windowMinSize.y){
        window.resize(window.getSize().x, Object.getSize().y)
    }
}
else{
    window.resize(windowMinSize);
}

我查看了 switch 和其他选项,但没有找到我要找的东西。

此处使用的正确模式是限制调用外部方法的代码路径的数量,以便只有一个使用点:

你的伪代码变成这样:

auto new_win_size = windowMinSize;
if (Object.getSize().x > window.getSize().x || Object.getSize().y > window.getSize.y){
    if(Object.getSize().x > windowMinSize.x){
        new_win_size.x = Object.getSize().x;
        new_win_size.y = window.getSize().y;
    }
    if(Object.getSize().y > windowMinSize.y){
        new_win_size.x = window.getSize().x;
        new_win_size.y = Object.getSize().y;
    }
}

window.resize(new_win_size );

但我怀疑你真正想要的是:

auto new_win_size = windowMinSize;

if(Object.getSize().x > windowMinSize.x){
    new_win_size.x = Object.getSize().x;
}
if(Object.getSize().y > windowMinSize.y){
    new_win_size.y = Object.getSize().y;
}

window.resize(new_win_size );

我有点困惑,因为最后听起来您只想将 window 的大小调整为对象大小,同时保持特定的最小大小。

因此,您只需要简单地调用 std::max() 来确定两个输入中较大的值:

unsigned int width = std::max(min_width, object_width);
unsigned int height = std::max(min_height, object_height);

使用新尺寸时,它基本上会调整 window 的大小以匹配您的对象,除非该对象小于您的最小尺寸。

您可以使用相同的模式来添加最大尺寸,使用 std::min() 作为附加层:

unsigned int width = std::min(max_width, std::max(min_width, object_width));
unsigned int height = std::min(max_height, std::max(min_height, object_height));

编辑: 正如 bolov 正确建议的那样,对于使用新 std::clamp:

的现代编译器,这可以进一步简化
unsigned int width = std::clamp(object_width, min_width, max_width);
unsigned int height = std::clamp(object_height, min_height, max_height);

您可以使用可变 templates 来获得您想要的。我这里的版本采用 else 函数(第一个参数),然后是函数对(谓词、块、谓词、块、...)。如果 none 个谓词的计算结果为 true,则将执行 else 函数。这应该适用于 C++11 或更高版本。

这里可能有一些错误(我没有做过太多测试),但你可以踢轮胎。如果将 #if 0 更改为 #if 1,您会发现 else 函数不会被调用。

#include <iostream>

template <typename PRED, typename EXEC>
bool multi_branch_detail(PRED pred, EXEC exec) {
    if(pred()) {
        exec();
        return true;
    }
    return false;
}

template <typename PRED, typename EXEC, typename ...REM>
bool multi_branch_detail(PRED pred, EXEC exec, REM ...rem) {
    auto result = false;
    if(pred()) {
        exec();
        result = true;
    }
    return multi_branch_detail(std::forward<REM>(rem)...) || result;
}

template <typename ELSE, typename ...FNS>
void multi_branch(ELSE el, FNS ...fns) {
    if(!multi_branch_detail(std::forward<FNS>(fns)...)) {
        el();
    }
}


int main() {
    multi_branch(
        []() { std::cout << "No cases\n"; },

#if 0
        []() { return 1 < 2; }, []() { std::cout << "first case\n"; },
        []() { return 10 < 20; }, []() { std::cout << "second case\n"; },
#endif
        []() { return 1 > 2; }, []() { std::cout << "bug\n"; }
    );
    return 0;
}

输出 #if 0:

No cases

输出 #if 1:

first case
second case

我将您的问题解释为询问如何重构以下代码:

if ( A )
{
     if ( B ) { X } else { Y }
     if ( C ) { Z } else { Y }
}
else { Y }

以避免重复 Y


一种方法是:

bool A = ....;
bool B = ....;
bool C = ....;

if      ( A && B ) { X }
else if ( A && C ) { Z }
else { Y }

尽管与原始代码的短路行为相比,这可能涉及不必要的函数调用。


最"obvious"的解决方案是存储一个变量:

bool updated = false;
if ( A ) 
{
    if ( B ) { X; updated = true; }
    if ( C ) { Z; updated = true; }
}
if ( !updated ) { Y }

另一种方法是使用可以突破的控制结构:

do
{
     if ( A )
     {
          if ( B ) { X; break; }
          if ( C ) { Z; break; }
     }
     Y;
} while (0);

有些人不喜欢这个,因为如果你正在阅读代码,它看起来像是我们进入了一个循环,但后来证明它不是一个循环。我个人会做一个函数(breakreturn 代替);但是如果使用 do...while(0) 你应该在开始处添加代码注释以指出这不是一个真正的循环。