我在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。
根据文章,它可以通过定义两个邪恶的宏来实现:
#define named(blockname) goto blockname; \
blockname##_skip: if (0) \
blockname:
#define break(blockname) goto blockname##_skip;
我知道很多人都想放弃使用goto
。我个人发现它在极少数情况下很有用,特别是当我想break
一堆嵌套循环时。这个成语在我看来是一个更清晰的解决方案,但是可以在实际代码中使用它吗?
在文章的讨论页面上,可以阅读:
“不要这样做。你最终会陷入地狱”
所以我的问题是:使用命名循环习惯用法有什么缺点?危险吗 ?如果是,为什么?
加分问题:是否可以类似地实现名为continue
的问题? (我认为使用named(...) for(...;...;...) {}
语法是不可能的,但谁知道呢?)
#define breakLoop()
呢?
答案 0 :(得分:10)
正如评论中所述,#define break
是有问题的。我们假设您使用其他东西。
我仍然认为这很危险。对于C ++程序员来说,这是一种非常不寻常的习惯,因此他们不太可能理解,因此他们可能会做出重大改变。鉴于完成同样事情的方法不那么令人惊讶 - 因此也就是那么危险 - 我会建议不要这样做。
考虑将循环放在函数或lambda中。然后你可以return
打破外循环。作为一个好处,您可以返回有关过早退出的信息,这可能对外部代码有用。
答案 1 :(得分:2)
我发现了一些问题。
首先,您要定义一个与该语言的保留字名称相同的宏。即使你的编译器不抱怨,它也容易出错,而且(IMO,至少)是危险的。
其次,我总是犹豫是否以编程方式创建标签。即使您的编译器可能会抱怨如果您在同一范围内意外创建了两个具有相同名称的标签,如果没有程序员解析这些宏(这部分地违背了额外抽象的目的),它可能不会很容易理解它生成的错误消息。
可能我的主要问题是宏引入了与普通语言语法不同的东西。 named(...)
行不以分号结尾,也不跟{ ... }
块后面。添加任何类型的新语法为开发人员混淆和意外滥用打开了大门。
总的来说,我有点像命名循环的想法,但这不是你想用宏创建的东西。这是一种真正需要由语言本身提供的机制。使用C或C ++时,使用手动创建的标签和goto
更清晰,更安全,更易于维护。显然比隐藏宏背后发生的事情更好。