如何编写一个针对布尔值(false和true)的`for`循环

57
一个有趣/好奇的问题:怎样在C++中编写一个for循环,它可以迭代两个bool值(即truefalse),只使用bool操作(即不进行其他类型的转换)?
背景是我想检查类似于(A && B) || (!B && !C && !D) == true这样的方程式有多少解,并开始编写像for (bool A=false; ??? ; ++A) for (bool B=false; ...)等等的东西,但我立刻被???卡住了——即继续循环的条件是什么?当然,我将它改写为使用int,并且我也知道do ... while循环将起作用,但我很好奇是否有可能编写这样的for循环呢?由于SO似乎没有答案,我决定询问:)
更新:请注意,在至少两个现已删除的答案中建议的“显然”的变体for(bool A=false; !A; A=true)只会运行一次迭代,因为对于第二个迭代,条件!A变为false,循环结束。
经过一些思考,我认为在C++03中不使用第二个变量或像Dietmar Kühl建议的基于指针的构造是不可能做到的。该条件应该被测试三次以实现所需的执行效果,因此只使用两个bool值是不够的。而do-while循环之所以起作用,是因为第一次迭代是无条件执行的,条件只被检查了两次,因此可以使用一个bool值来选择继续还是退出。

2
错误答案被迅速清除的速度真是惊人! - Mike Nakis
1
是的,你不讨厌他们这样做吗?我觉得这是一个好问题。我点了赞。 - Mike Nakis
因为这个网站不是用来消遣好奇心的,而是真正的提问。附言:不,不是我提出的问题,我也很好奇。 - Mr Lister
为什么会有踩票?这是一个合法的问题,也是一个有趣的好奇心。+1 - Kerrek SB
9个回答

70

在C++11中:for (bool b : { false, true }) { /* ... */ }


这是一个C++03版本的示例:

for (bool a = true, b = false; b != a; a = a && b, b = !b) { /*...*/ }

(使用ab中的任意一个。)


1
那是一个有着初始化列表的有趣版本。 - Xeo

8

当局限于使用C++2003时,您可以使用与C++2011方法大致相同的方法;

{
  bool const bools[] = { false, true };
  for (bool const* it(bools); it != std::end(bools); ++it) {
      bool a(*it);
      use(a);
  }
}

可能被打包在宏中。您还可以使用
for (bool a: { false, true }) {
    use(a);
}

我认为你可能可以将数组打包到for循环的声明部分中。不确定。 - Xeo
1
@Wolf: 这个回应是在2012年写的。我想过去的我没有假设C++11普遍可用(尽管我在1998年就发现了实现std::end()的必要功能)。 - Dietmar Kühl
太好了,你更新了它 :) 这将帮助我记住 std::end ;) - Wolf

5
a = true;
do {
  use(a);
  a = !a;
} while (!a);

好的,虽然它不是一个for循环,但我认为它比任何for循环建议更易读(当然,除了C++11方法)。


为了使 continue 在循环中按预期工作:a = false; do { ... } while (a = !a);。变量/常量的初始值:a = init; do { ... } while (init != (a = !a)); - adf88

5
for (int a = 0; a <= 1; a++)
  doStuff(a ? true : false);

请忘记“不能将数据类型转换为其他类型”的限制 :) 最终,清晰比人为的限制更重要。五年后,您将阅读自己的代码,并想知道“我当时在想什么,这是某种混淆竞赛吗?”


如果你使用 char/uint8_t 而不是 int,很有可能它们会编译成相同的底层数据类型,并且可以零成本地进行转换。 - Slipp D. Thompson
@SlippD.Thompson 这取决于 doStuff 是否高效使用 int - Wolf
或者你可以使用以下代码:for (int a = 0; a <= 1; a++) doStuff(a==1); - Epirocks

2

再来一条关于C++03的:

for(bool a = false, b = true; b; a = !a, b = a)  

使用 b。

2
这个答案讨论了“不可能”的 C++03,只针对单变量的解决方案。
首先,让我们确认没有一个确定性算术表达式仅依赖于单个输入变量,可以同时对输入 true 和 false 都为真,但对于第三个值不是 true 就是 false。
然而,我们可以“作弊”。虽然我要求您证明我实际上在作弊。
#include <iostream>

using namespace std;

int main() {
    for (bool b = false; *((char*)&b) != 2; *((char*)&b) += 1) {
        cout << "bool " << b << endl;
    }
}

这肯定看起来像未定义行为。C++03 对此有些不清楚。然而,sizeof 必须始终至少为1(对于长度为0的变长数组有一个非标准的例外)。此外,由于我们保证每个字符至少有8位,我们可以用第二个字符作为计数器。
实际上,要做到这一点,我们需要绕过确定性(如果不放弃我们迭代 false, true 恰好一次的保证,就无法做到)或者使用我们的限制类型系统。

1

这个也可以工作:

for (bool a = false, b = false; a == b; b = !b, a = a || b) { }

(与@KerrekSB的解决方案有些相反)


0

我知道你要求不进行类型转换的解决方案,但我想你的意思是“不进行不适当的类型转换”。在这种特定情况下,这里提供了一个替代bool的对象的答案。

struct IterableBool
{
  bool b;
  bool out_of_scope;
  IterableBool() : b(false), out_of_scope(false) {}
  IterableBool(bool b_) : b(b_), out_of_scope(false) {}
  IterableBool(IterableBool ib) : b(ib.b), out_of_scope(ib.out_of_scope) {}
  operator bool () { return this->b; }
  bool in_scope() const { return !this->out_of_scope; }
  IterableBool& operator ++ ()
  {                    
    this->out_of_scope = this->b;
    this->b = true;
    return *this;
  }
  IterableBool operator ++ (int)
  {
    IterableBool copy = *this;
    ++(*this);
    return copy;
  }
  IterableBool& operator -- ()
  {
    this->out_of_scope = !this->b;
    this->b = false;
    return *this;
  }
  IterableBool operator -- (int)
  {
    IterableBool copy = *this;
    --(*this);
    return copy;
  }
};

// Usage :
for(IterableBool ib = false;  ib.in_scope(); ++ib)
  do_stuff((bool)ib);

0
一个可能更易于理解的版本,基于Pokrovsky的回答:
bool firstPass=true;
for (int i = 0; i < 2; i++, firstPass=false)
    myfunc(firstPass);

或者更简单地说:

for (int pass = 1; i < 3; i++)
    myfunc(pass == 1);

或者甚至更好

for (int secondPass = 0; secondPass < 2; ++secondPass)
    myfunc(!secondPass);

(在Pokrovsky的答案进行了优化)

我认为当程序员在循环中看到变量时,让它有意义是很重要的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接