如何在c++中创建一个干净的级联if结构

How do I create a clean cascading if structure in c++?

本文关键字:一个 级联 结构 if c++ 创建      更新时间:2023-10-16

我正在使用boost的regex库,我发现确定是否找到了命名匹配,然后使用该信息有点烦人。要检测命名匹配,我想这样做:

typedef boost::match_result<string::const_iterator> matches_t;
typedef matches_t::const_reference match_t;
boost::regex re("(?:(?<type1>aaaa)|(?<type2>bbbb)" /*...*/ "|(?<typeN>abcdefg)");
string str(SOME_STRING);
matches_t what;
boost::match_flag_type flags = boost::match_default;
if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
  if((match_t type1 = what["type1"]).matched)
  {
     // do stuff with type1
  }
  else if((match_t type2 = what["type2"]).matched)
  {
     // do stuff with type2
  }
  // ...
  else if((match_t typeN = what["typeN"]).matched)
  {
     // do stuff with typeN
  }
}

如果那可行,那就太好了。作用域将被限制到if的主体,内存可以被有效地使用,并且看起来相当干净。遗憾的是,它不起作用,因为您不能在列表中定义变量。(

这是有可能的:

if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
  match_t found = what["type1"];
  if(found.matched)
  {
     // do stuff with type1
  }
  else if((found = what["type2"]).matched)
  {
     // do stuff with type2
  }
  // ...
  else if((found = what["typeN"]).matched)
  {
     // do stuff with typeN
  }
}

但是match_t是一个const引用,所以它不能赋值。(tl;dr)我也不知道底层类型是什么,通常我真的不想知道,因为我更喜欢一个更通用的解决方案,我可以在这个正则表达式的例子之外使用。甚至std::move()也被用于[…]它变得更加冗长,文档并没有说它使用sub_match的移动语义。当然,由于本段第一句给出的原因,所有这些都是没有意义的。

另一个选项是这样做:

if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
  match_t type1 = what["type1"];
  if(type1.matched)
  {
     // do stuff with type1
  }
  else {
    match_t type2 = what["type2"];
    if(type2.matched)
    {
       // do stuff with type2
    }
    // ...
          else {
            match_t typeN = what["typeN"];
            if((match_t typeN = what["typeN"]).matched)
            {
               // do stuff with typeN
            }
          }
    // ...
    }
  }
}

我不喜欢它,因为大括号嵌套很深。

可能滥用了一个循环结构,break s位于每个if体的末尾,像这样:

if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
  do{
    {
      match_t type1 = what["type1"];
      if(type1.matched)
      {
         // do stuff with type1
         break;
      }
    }
    {
      match_t type2 = what["type2"];
      if(type2.matched)
      {
         // do stuff with type2
         break;
      }
    }
    // ...
    {
      match_t typeN = what["typeN"];
      if(typeN.matched)
      {
         // do stuff with typeN
         break;
      }
    }
  } while(0);
}

这是更好的,但仍然不是伟大的。使用宏,可以隐藏大部分噪声。如:

#define IF(declare, cond) do{{declare;if(cond){                
#define ELSE_IF(declare, cond) break;}}{declare; if(cond){     
#define ELSE break;}}{{                                        
#define END_IF break;}}}while(0);                              
if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
  IF(match_t type1 = what["type1"], type1.matched)
  {
     // do stuff with type1
  }
  ELSE_IF(match_t type2 = what["type2"], type2.matched)
  {
     // do stuff with type2
  }
    // ...
  ELSE_IF(match_t typeN = what["typeN"], typeN.matched)
  {
     // do stuff with typeN
  }
  END_IF
}

大括号实际上是由宏隐含的,但是通过重述它们可以使阅读更清晰。

我能想到的另一个选择是进入boost::sub_match类并添加一个转换函数将该类型转换为bool类型,其返回值将是matched成员的返回值。然后我可以在if表达式中声明一个match_t变量,它会被if自动转换为一个布尔值。我不确定我是否已经做到了,而且它不是通用的。

在风格上,是我认为好的或坏的(实际上只有最后三个是有效的,所以我可能会限制评论他们)。

还有,谁有更好的建议?请说明为什么你认为他们更好。

通常建议避免嵌套if -它们使代码更难阅读。如果有嵌套的If,可能应该用函数调用来替换。

在你的例子中,你需要使用循环。

你的第二个例子:

if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
  match_t found = what["type1"];
  if(found.matched)
  {
     // do stuff with type1
  }
  else if((found = what["type2"]).matched)
  {
     // do stuff with type2
  }
  // ...
  else if((found = what["typeN"]).matched)
  {
     // do stuff with typeN
  }
}

BEGS for循环:

const char *types[] = {"type1", "type2", "typeN", 0};
for(const char **cur = types; *cur; cur++){
    found = what[*cur];
    if (found.matched){
         //initiate robot uprising
         break;
    }
}

你所有的其他例子(IMO)都是一个糟糕的编码风格。我喜欢保持循环,它很短。如果它不适合20行代码,那么它最好是做一些非常复杂的事情(这不是你的情况)。如果它没有做任何复杂的事情,它需要被重新构造。

您可以这样做(注意此代码没有针对编译器进行测试)

// create a map between match types and actions
std::map<std::string, std::function<match_t>> actions;
actions["type1"] = [&](match_t type) {...};
// iterate through the actions map and check each match type
for(auto kvp : actions)
{
   match_t type = what[kvp.first];
   if(type.matched)
   {
      // do stuff with this result
      kvp.second(type);
   }
}

你可以写一个match_t的换行和operator bool的重载:

struct bmatch
{
    matches_t::const_reference ref;
    bmatch(matches_t::const_reference r)
    :ref(r)
    {}
    operator bool() const
    {
        return ref.matched;
    }
};

然后:

if (bmatch type1 = what["type1"])
{ //Use type2.ref
}
else if (bmatch type2 = what["type2"])
{ //Use type2.ref
}
对于额外的分数,你也可以重载operator->:
    matches_t::const_reference operator->() const
    {
        return ref;
    }