优化三元运算符

时间:2015-10-20 14:39:02

标签: c++ ternary-operator conditional-operator

我遇到了其他人写的这段代码。推荐使用条件运算符还是常用?我觉得它不太可维护 - 或者只是我?有没有其他的写作方式?

  exp_rsp_status =  req.security_violation ? (dis_prot_viol_rsp && is_mstr) ? 
                    uvc_pkg::MRSP_OKAY : uvc_pkg::MRSP_PROTVIOL : req.slv_req.size()  ?
                    ((is_mst_abort_rsp && dis_mst_abort_rsp) ||
                    ((req.slv_req[0].get_rsp_status()==uvc_pkg::MRSP_PROTVIOL) && dis_prot_viol_rsp) ||
                    (is_mst_abort_rsp && req.is_pci_config_req() && dis_pcicfg_mst_abort_rsp)) ?
                    uvc_pkg::MRSP_OKAY : req.slv_req[0].get_rsp_status() : uvc_pkg::MRSP_OKAY;

6 个答案:

答案 0 :(得分:66)

那可怕的代码。

  • 格式错误。我没有看到表达式的层次结构。
  • 即使它具有良好的格式,表达式也会太复杂,无法用人眼快速解析。
  • 目的不明确。这些条件的目的是什么?

那你能做什么?

  • 使用条件语句(if)。
  • 提取子表达式,并将它们存储在变量中。从重构目录中查看this一个很好的例子。
  • 使用辅助函数。如果逻辑很复杂,请使用早期return。没有人喜欢深刻的缩进。
  • 最重要的是,给一切都一个有意义的名字。意图应该清楚为什么必须计算一些东西。

只是要明确:三元运算符没有错。如果用得很明智,它通常会产生更容易消化的代码。尽管避免嵌套它们。如果代码清晰,我偶尔使用第二级,即使这样我也使用括号,所以我的可怜的大脑不必做额外的循环来取消运算符优先级。

关心代码的读者。

答案 1 :(得分:29)

也许这是在设备驱动程序的消息循环中,原始编码器(可能是10年前)并不想要在代码中跳转。我希望他证实他的编译器没有用跳跃实现三元运算符!

检查代码,我的第一句话是,一系列三元运算符 - 就像所有代码一样 - 在充分格式化时更易读。

那就是说,我不确定我是否正确解析了OP的例子,这反映了它。即使是传统的嵌套if-else结构也难以验证。这个表达式违反了基本的编程范式:Divide and Conquer。

function index($id = ""){
    if($id) { 
        // fetch data from db and pass it to view
    } else {
        // pass empty value to view
    }
    $data['items'] = $this->crud->read('items');
    $data['header_info'] = $this->crud->header_info('items');
    $this->load->view('items_view', $data);


}

我想检查代码在重构时的外观。它确实不短,但我喜欢说话功能名称如何使意图更清晰(当然我在这里猜测)。这在某种程度上是伪代码,因为变量名称可能不是全局的,因此函数必须具有参数,使得代码不再清晰。但也许参数可以是指向状态或请求结构的单个指针等(从中提取了req.security_violation ? dis_prot_viol_rsp && is_mstr ? uvc_pkg::MRSP_OKAY : uvc_pkg::MRSP_PROTVIOL : req.slv_req.size() ? is_mst_abort_rsp && dis_mst_abort_rsp || req.slv_req[0].get_rsp_status()==uvc_pkg::MRSP_PROTVIOL && dis_prot_viol_rsp || is_mst_abort_rsp && req.is_pci_config_req() && dis_pcicfg_mst_abort_rsp ? uvc_pkg::MRSP_OKAY : req.slv_req[0].get_rsp_status() : uvc_pkg::MRSP_OKAY; 之类的值)。在结合不同条件时是否使用三元组是有争议的。我觉得它很优雅。

dis_prot_viol_rsp

最后,我们可以利用这样一个事实:bool ismStrProtoViol() { return dis_prot_viol_rsp && is_mstr; } bool isIgnorableAbort() { return is_mst_abort_rsp && dis_mst_abort_rsp; } bool isIgnorablePciAbort() { return is_mst_abort_rsp && req.is_pci_config_req() && dis_pcicfg_mst_abort_rsp; } bool isIgnorableProtoViol() { return req.slv_req[0].get_rsp_status()==uvc_pkg::MRSP_PROTVIOL && dis_prot_viol_rsp; } eStatus getRspStatus() { eStatus ret; if( req.security_violation ) { ret = ismStrProtoViol() ? uvc_pkg::MRSP_OKAY : uvc_pkg::MRSP_PROTVIOL; } else if( req.slv_req.size() ) { ret = isIgnorableAbort() || isIgnorableProtoViol() || isIgnorablePciAbort() ? uvc_pkg::MRSP_OKAY : req.slv_req[0].get_rsp_status(); else { ret = uvc_pkg::MRSP_OKAY; } return ret; } 是默认的,只在某些情况下被覆盖。这消除了分支。看看经过一些凿刻后,代码的推理很明显:如果不是安全违规,请查看实际的请求状态,减去空请求和可忽略的中止。

uvc_pkg::MRSP_OKAY

答案 2 :(得分:14)

多么丑陋的一团糟。我把它分解成if和else只是为了看看它在做什么。没有更多的可读性,但我想我会发布它。希望别人能为您提供更优雅的解决方案。但要回答你的问题,不要使用复杂的三元组。没有人愿意做我刚刚做的事情来弄清楚它在做什么。

if ( req.security_violation )
{
    if ( dis_prot_viol_rsp && is_mstr )
    {
        exp_rsp_status = uvc_pkg::MRSP_OKAY;
    }
    else
    {
        exp_rsp_status = uvc_pkg::MRSP_PROTVIOL;
    }
}
else if ( req.slv_req.size() )
{
    if ( ( is_mst_abort_rsp && dis_mst_abort_rsp ||
         ( req.slv_req[0].get_rsp_status() == uvc_pkg::MRSP_PROTVIOL && dis_prot_viol_rsp ) ||
         ( is_mst_abort_rsp && req.is_pci_config_req() && dis_pcicfg_mst_abort_rsp ) )
    {
        exp_rsp_status = uvc_pkg::MRSP_OKAY;
    }
    else
    {
        exp_rsp_status = req.slv_req[0].get_rsp_status();
    }

}
else
{
    exp_rsp_status = uvc_pkg::MRSP_OKAY
} 

答案 3 :(得分:10)

这是一个糟糕的代码。

虽然通常需要使用单个表达式初始化变量(例如,我们可以将其设为const),但这不是编写这样的代码的借口。您可以将复杂逻辑移动到函数中并调用它来初始化变量。

void
example(const int a, const int b)
{
  const auto mything = make_my_thing(a, b);
}

在C ++ 11及更高版本中,您还可以使用lambda初始化变量。

void
example(const int a, const int b)
{
  const auto mything = [a, b](){
      if (a == b)
        return MyThing {"equal"};
      else if (a < b)
        return MyThing {"less"};
      else if (a > b)
        return MyThing {"greater"};
      else
        throw MyException {"How is this even possible?"};
  }();
}

答案 4 :(得分:4)

其他人已经说过代码摘录有多糟糕,有很好的解释。我将提供更多原因,为什么代码不好:

  1. 如果你考虑一个&#34; if-else&#34;要实现一个功能,那么很明显代码是多么复杂。在你的情况下,我甚至无法计算ifs的数量。

  2. 很明显,您的代码突破了single responsibility principle,告诉:

      

    ......一个类或模块应该有一个,而且只有一个改变的理由。

  3. 单元测试将是一场噩梦,这是另一个危险信号。我打赌你的同事甚至没有尝试为这段代码编写单元测试。

答案 5 :(得分:0)

普通或推荐?否。

我做了类似的事,但我有我的理由:

  1. 这是第三方C函数的论据。
  2. 当时我并不精通现代C ++。
  3. 我评论并格式化了它的f ***因为我知道除了我以外的某人会读它...或者我需要知道它在几年之后做了什么。
  4. 这是DEBUG CODE,从未进入发布阶段。

    textprintf_ex(gw->GetBackBuffer(), font, 0, 16, WHITE, -1, "BUTTON: %s",
                           //If...                        Then Display...
                      (ButtonClicked(Buttons[STOP])    ?  "STOP"
                    : (ButtonClicked(Buttons[AUTO])    ?  "AUTO" 
                    : (ButtonClicked(Buttons[TICK])    ?  "TICK"
                    : (ButtonClicked(Buttons[BLOCK])   ?  "BLOCK"
                    : (ButtonClicked(Buttons[BOAT])    ?  "BOAT"
                    : (ButtonClicked(Buttons[BLINKER]) ?  "BLINKER"
                    : (ButtonClicked(Buttons[GLIDER])  ?  "GLIDER"
                    : (ButtonClicked(Buttons[SHIP])    ?  "SHIP"
                    : (ButtonClicked(Buttons[GUN])     ?  "GUN"
                    : (ButtonClicked(Buttons[PULSAR])  ?  "PULSAR"
                    : (ButtonClicked(Buttons[RESET])   ?  "RESET"
                    :  /*Nothing was clicked*/            "NONE"
                    )))))))))))
                 );
    
  5. 我没有使用if-else链的唯一原因是它会使代码变得更加难以理解,因为我需要做的就是在屏幕上打印一个单词。