Каковы обычные операторы Flag? - PullRequest
0 голосов
/ 10 ноября 2018

Я определил перечисление, которое я хочу использовать для хранения флагов в переменных. Цель состоит в том, чтобы иметь возможность установить несколько флагов и проверить, какие из них установлены.

Для этой цели мне понадобятся две операции: одна для определения, если a в b, и вторая, чтобы выяснить, не a не в b. Я реализовал их, используя перегрузки операторов. Я выбрал <= и != произвольно:

enum Flag { NoFlags, B, C, D=4 };

inline Flag operator |(Flag a, Flag b) {
    return static_cast<Flag >(static_cast<int>(a) | static_cast<int>(b)); }

inline bool operator <=(Flag a, Flag b) { return a == (a & b); }

inline bool operator !=(Flag a, Flag b) { return !(a<=b); }

Это отлично работает. Так, например:

Flag flags1 = A|C;
Flag flags2 = B|D;
Flag flags3 = NoFlags|A|B|C|D;

A <= flags1 //true
A != flags2 //true
C != flags3 //false
B <= flags1 //false

Я выбрал <= и != произвольно. Я знаю, что они обычно имеют совершенно разные значения. Вот мне и интересно:

  • Существует ли общепринятая практика использования других операторов для представления этих двух операций?

  • Или было бы лучше использовать функции вместо перегрузок операторов?

1 Ответ

0 голосов
/ 10 ноября 2018

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

  • !(a <= b) ожидается равным a>b
  • !(a != b) ожидается равным a==b
  • flags1 != flags3 больше не будет означать, что в обеих переменных установлены одинаковые флаги

По этой причине и в соответствии с принципом наименьшего удивления я бы настоятельно советовал против вашего выбора.

Обычная практика использования операторов для флагов заключается в использовании | для установки флагов и & в сочетании с ~ для сброса флагов и & для проверки флагов. Но это сделано, предполагая предсказуемые побитовые операции, а не интерфейс более высокого уровня, как ваш.

В вашем случае я бы рекомендовал использовать функции вместо операторов и позволить именам функций четко выражать ваше намерение (например, is_included()). Я бы даже предложил рассмотреть перечислимые классы с функциями-членами для лучшей инкапсуляции.

Поскольку вы, похоже, рассматриваете свои Flag переменные как набор комбинированных отдельных флагов, вы можете получить вдохновение от интерфейса std::set, если вы ищете последовательную и известную схему именования ( например, insert(), find(), contains(), merge() и т. д.).

...