【问题标题】:The named loop idiom : dangerous?命名循环成语:危险?
【发布时间】:2012-06-12 22:32:59
【问题描述】:

我读过一篇关于 C++ 中“命名循环成语”的文章:http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Named_Loop

这个成语让我们可以这样写:

named(outer) 
for(int i = 0 ; i < rows ; ++i) {

   named(inner) 
   for(int j = 0 ; j < cols ; ++j) {

        if(some_condition)
            break(outer);   // exit the 'outer' loop 

   }
}

这种结构已经作为许多语言的核心特性存在,例如 Java。

根据文章,可以通过定义两个邪恶宏在C++中实现:

#define named(blockname) goto blockname; \
                         blockname##_skip: if (0) \
                         blockname:

#define break(blockname) goto blockname##_skip;

我知道很多人想禁止使用goto。我个人发现它在极少数情况下很有用,尤其是当我想break 一堆嵌套循环时。这个成语在我看来是一个更干净的解决方案,但可以在实际代码中使用它吗?

在文章的讨论页面,可以阅读:

“不要这样做。你会下地狱的”

所以我的问题是:使用命名循环习语有什么缺点?危险吗 ?如果是,为什么?

额外问题:是否可以类似地实现命名continue? (我认为使用named(...) for(...;...;...) {} 语法是不可能的,但谁知道呢?)

编辑:我同意你的观点,重新定义关键字很讨厌。改用#define breakLoop() 怎么样?

【问题讨论】:

  • #define break 对我个人来说是个彻底的破坏者。
  • #define break(something) 和#define break 不一样,它不能代替正常的break语句。
  • #define break 介于未定义行为和格式错误之间。将它定义为类函数宏并不重要,唯一重要的是宏的名称在词法上与关键字相同。
  • 它可以破坏任何东西,因为it is forbidden(至少在使用标准库头文件的源文件中)。
  • 如果你想使用goto,那么就让它保持可见,而不是用宏来掩盖它。

标签: c++ c


【解决方案1】:

正如 cmets 中所述,#defining break 是有问题的。假设您使用其他东西。

我仍然认为这很危险。这是一个非常不寻常的习语(对于 C++ 程序员),因此他们不太可能理解,因此他们可能会做出重大更改。鉴于完成同一件事的方式不那么令人惊讶(因此也不太危险),我建议不要这样做。

考虑将循环放在函数或 lambda 中。然后你可以return 跳出外循环。作为一个好处,您可以返回有关过早退出的信息,这可能对外部代码有用。

【讨论】:

  • 确实 lambda 非常适合。我想知道他们是否不会阻止某种优化(循环不变量),但我只是在猜测......
  • @Frédéric : std::for_each + lambdas 是为此制作的。 ;-](如果您真的需要索引而不是迭代器,请添加 boost::counting_iterator。)
  • 确实 :p 但是这个解决方案不像“命名循环”那样灵活,因为你不能跳转到任何外部循环:你只能用return 退出 lambda 范围。
  • @Frédéric:这也恰好更容易推理。谁最终维护了你的代码,谁会感谢你。 ;-]
  • 我知道我错了,但我喜欢探索 C++ 的阴暗面……可怜的维护者 :)
【解决方案2】:

我发现这有几个问题。

首先,您要定义一个与语言的保留字同名的宏。即使您的编译器不抱怨这一点,它也容易出错并且不会(至少是 IMO)危险。

其次,我总是不愿以编程方式创建标签。即使您的编译器可能会抱怨如果您不小心在同一范围内创建了两个具有相同名称的标签,但如果程序员不剖析这些宏(这部分违背了额外抽象的目的),它生成的错误消息可能不会很容易理解.

可能我的主要问题是宏引入了一些与普通语言语法不同的东西。 named(...) 行不以分号结尾,也不跟{ ... } 块。添加任何类型的新语法都会为开发人员的困惑和意外误用打开大门。

总的来说,我有点喜欢命名循环的想法,但这不是您想要使用宏创建的那种东西。这是一种真正需要由语言本身提供的机制。使用 C 或 C++ 时,使用手动创建的标签和 goto 更清洁、更安全且更易于维护。明确表达几乎总是比隐藏宏背后发生的事情要好。

【讨论】:

  • 创建两个具有相同名称的标签并不比创建两个具有相同名称的变量更有可能:) C++ 没有内置的标记中断/继续是一种耻辱...
  • @FrédéricTERRAZZONI- 是的。但是,将某些东西的创建隐藏在在编译时构建名称的宏后面比公开声明更可能导致重复定义。
猜你喜欢
  • 2011-11-08
  • 1970-01-01
  • 2012-04-24
  • 2021-12-01
  • 2012-07-18
  • 2016-02-04
  • 2012-02-13
  • 1970-01-01
  • 2013-03-21
相关资源
最近更新 更多