Как написать цикл `for` над значениями bool (false и true)

Вопрос в основном для удовольствия/любопытства: как написать цикл for в С++, который будет перебирать два значения a bool (т.е. true и false), используя только операции с 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, и цикл завершается.

После некоторого размышления я считаю невозможным сделать это в С++ 03 без второй переменной или конструкцией, основанной на указателях, как предложено Dietmar Kühl. Условие должно быть проверено три раза в желаемом исполнении, поэтому двух значений bool просто недостаточно. И цикл do-while работает, потому что первая итерация выполняется безоговорочно, условие проверяется только дважды, поэтому значение bool можно использовать для выбора между продолжением и выходом.

Ответ 1

В С++ 11: for (bool b : { false, true }) { /* ... */ }


Здесь версия С++ 03:

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

(Используйте либо a, либо b.)

Ответ 2

При ограничении на С++ 2003 вы можете использовать подход, который примерно эквивалентен подходу С++ 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);
}

Ответ 3

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

И забудьте о ограничениях "без конверсий на другие типы":) В конце дня ясность важнее искусственных ограничений. Через пять лет вы будете читать свой собственный код и задаваться вопросом: "Какого черта я думал, это какой-то конкурс по обфускации?"

Ответ 4

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

ОК, так что это не цикл for, но я бы сказал, что он более читабельен, чем любой из предложений for loop (за исключением подхода С++ 11, конечно.)

Ответ 5

Еще один для С++ 03:

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

Используйте b.

Ответ 6

В этом ответе рассматривается "невозможное" решение С++ 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;
    }
}

Это, безусловно, похоже на поведение undefined. С++ 03 немного неясно об этом. Однако sizeof всегда должно быть не менее 1 (с нестандартным исключением для массивов var-len длиной 0 строк). Более того, поскольку мы гарантируем, что каждый char имеет не менее 8 бит, мы можем использовать второй для нашего счетчика.

В самом деле, для этого нам нужно либо ограничить детерминизм (не может, не отказываясь от гарантии, которую мы итерируем по false, true ровно один раз), или нашей сдерживающей системы типов.

Ответ 7

Это тоже работает:

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

(вид инвертированного решения, чем @KerrekSB)

Ответ 8

Я знаю, что вы попросили решение без конвертации в другой тип, но я полагаю, вы имеете в виду "без преобразования в нераспределенный другой тип". Вот ответ, предлагающий объект, заменяющий 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);