C++のOR演算子について

AI実装検定のご案内

C++で「OR」を表す演算子には、主に次の2種類があります。

  • 論理OR演算子:||
  • ビットOR演算子:|

どちらも「OR」という名前が付いていますが、用途や動作は大きく異なります。

論理OR演算子の || は、複数の条件のうち、少なくとも1つが成立しているかを判定するときに使用します。

一方、ビットOR演算子の | は、整数が持つ各ビットを比較し、ビット単位でOR演算を行うための演算子です。

この2つは見た目がよく似ているため、混同しないように注意する必要があります。

目次

論理OR演算子「||」の基本

論理OR演算子 || は、複数の条件のうち、少なくとも1つが true であるかを判定します。

基本的な書き方

条件式1 || 条件式2

左側と右側のどちらか一方でも true であれば、式全体の結果は true になります。

両方とも false の場合だけ、結果は false です。

論理ORの真理値表

左側の条件右側の条件結果
falsefalsefalse
falsetruetrue
truefalsetrue
truetruetrue

論理ORの使用例

#include <iostream>

int main() {
    int age = 20;
    bool hasPermission = false;

    if (age >= 18 || hasPermission) {
        std::cout << "利用できます\n";
    } else {
        std::cout << "利用できません\n";
    }

    return 0;
}

この条件式では、次のどちらかが成立すれば処理が実行されます。

age >= 18
hasPermission

age は20なので、age >= 18true です。

そのため、hasPermissionfalse であっても、条件式全体は true になります。

論理ORでは短絡評価が行われる

組み込みの論理OR演算子 || では、短絡評価が行われます。

短絡評価とは

短絡評価とは、左側の条件だけで式全体の結果が確定した場合に、右側の条件を評価しない仕組みです。

論理ORでは、左側が true になった時点で、式全体が必ず true になります。

そのため、右側の式を確認する必要がありません。

短絡評価の例

#include <iostream>

bool check() {
    std::cout << "check関数が実行されました\n";
    return true;
}

int main() {
    bool result = true || check();

    std::cout << std::boolalpha << result << '\n';

    return 0;
}

左側の値がすでに true なので、右側の check() は実行されません。

出力結果は次のようになります。

true

次のメッセージは表示されません。

check関数が実行されました

左側がfalseの場合

左側が false の場合は、右側の結果を確認しなければ、式全体の結果を判断できません。

bool result = false || check();

この場合は、check() が実行されます。

短絡評価を利用した安全な条件判定

短絡評価は、処理を省略するだけではなく、安全なプログラムを書くためにも利用できます。

ヌルポインタを確認する例

if (ptr == nullptr || ptr->value == 0) {
    // 処理
}

この条件式では、最初に ptr == nullptr が評価されます。

ptrnullptr の場合、左側が true になるため、右側の次の式は評価されません。

ptr->value == 0

これにより、ヌルポインタを通してメンバーへアクセスする危険を避けられます。

条件の順番を逆にすると危険

次のように条件の順番を逆にすると、安全ではありません。

if (ptr->value == 0 || ptr == nullptr) {
    // 危険
}

このコードでは、最初に ptr->value が評価されます。

ptrnullptr の場合でも、先にメンバーへアクセスしようとするため、未定義動作につながります。

ポインタを使用する場合は、先にポインタが有効かどうかを確認します。

if (ptr == nullptr || ptr->value == 0) {
    // 安全
}

3つ以上の条件をORでつなぐ方法

論理OR演算子は、3つ以上の条件をつなげることもできます。

if (score >= 90 || hasSpecialPermission || isAdministrator) {
    std::cout << "アクセスを許可します\n";
}

この場合、次の条件のうち、1つでも true であれば処理が実行されます。

  • 点数が90点以上
  • 特別な許可を持っている
  • 管理者である

組み込みの || は左側から評価され、途中で true になった時点で、それ以降の条件は評価されません。

例えば、次のコードを考えます。

if (checkScore() || checkPermission() || checkAdministrator()) {
    // 処理
}

checkScore()true を返した場合、checkPermission()checkAdministrator() は実行されません。

数値を論理ORで評価した場合

C++では、数値を条件式として使用できます。

数値がboolへ変換されるルール

整数値は、条件式の中で次のように扱われます。

  • 0false
  • 0以外true

数値を使った例

#include <iostream>

int main() {
    int a = 0;
    int b = 5;

    if (a || b) {
        std::cout << "少なくとも一方は0以外です\n";
    }

    return 0;
}

a は0なので falseb は5なので true として扱われます。

そのため、条件式全体は true になります。

意図を明確に書く方法

数値をそのまま条件式に使用することもできますが、比較の意図を分かりやすくするため、次のように記述する場合もあります。

if (a != 0 || b != 0) {
    std::cout << "少なくとも一方は0以外です\n";
}

こちらのほうが、「0以外であるかを調べている」と明確に伝わります。

ポインタを論理ORで評価した場合

ポインタも条件式として使用できます。

ポインタは、条件式の中でおおむね次のように扱われます。

  • ヌルポインタ:false
  • ヌルポインタではないポインタ:true
int value = 10;
int* ptr = &value;

if (ptr || value > 100) {
    std::cout << "条件が成立しました\n";
}

この例では、ptr が有効なアドレスを保持しているため、true として扱われます。

論理ORの結果はbool型になる

組み込みの論理OR演算子 || の結果型は bool です。

int a = 10;
int b = 0;

bool result = a || b;

a は0以外なので trueb は0なので false として扱われます。

その結果、result には true が格納されます。

a の値である10がそのまま返されるわけではありません。

autoで受け取った場合

int a = 10;
int b = 0;

auto result = a || b;

この場合も、result の型は bool になります。

オーバーロードされた論理ORの注意点

C++では、クラス型などに対して operator|| をオーバーロードできます。

ただし、オーバーロードされた operator|| では、組み込みの || と同じ短絡評価は行われません。

組み込みの論理OR

if (conditionA || conditionB) {
}

組み込みの || では、conditionAtrue なら conditionB は評価されません。

オーバーロードされた論理OR

ユーザー定義型に対して operator|| を定義した場合は、通常の関数呼び出しに近い形で処理されます。

そのため、短絡評価を期待してはいけません。

初心者向けの一般的なコードではあまり登場しませんが、独自クラスを扱う場合には注意が必要です。

ビットOR演算子「|」の基本

ビットOR演算子 | は、整数の各ビットに対してOR演算を行います。

基本的な書き方

値1 | 値2

対応するビットのどちらか一方でも1であれば、結果のビットは1になります。

ビットORの真理値表

左側のビット右側のビット結果
000
011
101
111

ビットORは、両方のビットが1の場合も結果が1になります。

ビットORの基本的な計算例

#include <iostream>

int main() {
    int a = 5;
    int b = 3;

    int result = a | b;

    std::cout << result << '\n';

    return 0;
}

5と3を2進数で表すと、次のようになります。

5 = 0101
3 = 0011

対応するビットごとにOR演算を行います。

  0101
| 0011
------
  0111

0111 は10進数で7です。

そのため、出力結果は次のようになります。

7

論理ORとビットORの違い

||| は見た目が似ていますが、処理内容は異なります。

演算子名称主な用途短絡評価
``論理OR
``ビットORビット操作

数値を使った比較例

int a = 2;
int b = 4;

bool logicalResult = a || b;
int bitResult = a | b;

論理ORの場合、2と4はどちらも0以外なので、両方とも true として扱われます。

logicalResult == true

一方、ビットORでは、数値を2進数にしてビット単位で計算します。

2 = 0010
4 = 0100
  0010
| 0100
------
  0110

0110 は10進数で6です。

bitResult == 6

ビットORには短絡評価がない

ビットOR演算子 | では、左右のオペランドが両方とも評価されます。

関数を使った例

#include <iostream>

bool leftCheck() {
    std::cout << "leftCheck\n";
    return true;
}

bool rightCheck() {
    std::cout << "rightCheck\n";
    return false;
}

int main() {
    bool result = leftCheck() | rightCheck();

    std::cout << std::boolalpha << result << '\n';

    return 0;
}

このコードでは、leftCheck()true を返しても、rightCheck() も実行されます。

ただし、左右の関数がどちらから先に実行されるかに依存するコードを書くべきではありません。

重要なのは、| では左右の式が両方とも評価されるという点です。

論理ORを使用した場合

bool result = leftCheck() || rightCheck();

組み込みの || では、まず左側の leftCheck() が実行されます。

leftCheck()true を返すと、式全体が true と確定するため、rightCheck() は実行されません。

通常の条件判定では || を使用し、ビット操作を行う場合に | を使用します。

bool型にビットORを使用した場合

bool 型の値にも | を使用できます。

bool a = true;
bool b = false;

bool result = a | b;

この場合、結果は true になります。

ただし、| では左右の式が両方とも評価されます。

条件判定が目的であれば、通常は次のように || を使用します。

bool result = a || b;

複合代入演算子「|=」

ビットORには、代入を組み合わせた |= 演算子があります。

基本的な書き方

a |= b;

単純な変数であれば、おおむね次の式と同じ結果になります。

a = a | b;

使用例

#include <iostream>

int main() {
    unsigned int flags = 1;
    flags |= 4;

    std::cout << flags << '\n';

    return 0;
}

2進数で表すと、次のようになります。

1 = 0001
4 = 0100
  0001
| 0100
------
  0101

0101 は10進数で5なので、出力結果は5です。

複雑な左辺では違いがある

a |= ba = a | b は、単純な変数では同じ結果になります。

ただし、左辺が関数呼び出しなどを含む複雑な式の場合、完全に同じ動作になるとは限りません。

array[getIndex()] |= mask;

複合代入演算子では、左辺は一度だけ評価されます。

これを次のように書き換えると、getIndex() が複数回呼び出される可能性があります。

array[getIndex()] = array[getIndex()] | mask;

そのため、複合代入演算子は単なる省略記法ではなく、左辺の評価回数にも違いがあります。

ビットORを使ったフラグ管理

ビットORは、複数の設定や権限を1つの整数で管理するときによく使用されます。

フラグを定義する

constexpr unsigned int Read    = 1u << 0;
constexpr unsigned int Write   = 1u << 1;
constexpr unsigned int Execute = 1u << 2;

各フラグは、それぞれ異なる位置のビットを1にしています。

Read    = 001
Write   = 010
Execute = 100

フラグを追加する

unsigned int permissions = 0;

permissions |= Read;
permissions |= Write;

最初に Read を追加すると、次の状態になります。

000 | 001 = 001

さらに Write を追加すると、次のようになります。

001 | 010 = 011

これにより、permissions には読み取り権限と書き込み権限の両方が設定されます。

複数のフラグをまとめて設定する

permissions |= Read | Write;

この式は、次のように解釈されます。

permissions |= (Read | Write);

最初に ReadWrite をORで結合し、その結果を permissions に追加しています。

設定されたフラグを確認する方法

フラグが設定されているかを確認する場合は、ビットAND演算子 & を使用します。

if ((permissions & Read) != 0) {
    std::cout << "読み取り権限があります\n";
}

例えば、permissions が次の状態だとします。

permissions = 011
Read        = 001

AND演算を行うと、次のようになります。

  011
& 001
-----
  001

結果が0ではないため、Read フラグが設定されていると判断できます。

フラグを解除する方法

OR演算は、指定したビットを1にする操作には適しています。

ただし、すでに1になっているビットを0に戻すことはできません。

フラグを解除する場合は、ビットNOT演算子 ~ とビットAND演算子 & を組み合わせます。

permissions &= ~Write;

この処理では、Write に対応するビットだけを0にします。

フラグ解除の仕組み

簡略化して3ビットで考えると、次のようになります。

Write       = 010
~Write      = 101
permissions = 111

AND演算を行います。

  111
& 101
-----
  101

中央の Write ビットだけが0になります。

フラグには符号なし整数型を使う

単純なビットORの計算では、int を使用しても問題ありません。

int result = 5 | 3;

ただし、フラグやビットマスクを扱う場合は、一般に符号なし整数型を使用するほうが分かりやすくなります。

unsigned int flags = 0;

固定幅の整数型を使用することもできます。

#include <cstdint>

std::uint32_t flags = 0;

符号なし整数型を使用すると、符号ビットを意識せずにビット操作を行いやすくなります。

小さい整数型をビットORする際の注意点

unsigned char などの小さい整数型にビット演算を行うと、整数拡張によって結果が int 型になる場合があります。

unsigned char a = 0x01;
unsigned char b = 0x02;

auto result = a | b;

この場合、result の型は通常 int になります。

元の型へ格納したい場合は、必要に応じて明示的に変換します。

unsigned char result =
    static_cast<unsigned char>(a | b);

初心者向けの単純なフラグ管理では、最初から unsigned intstd::uint32_t を使用すると扱いやすくなります。

代替トークン「or」と「bitor」

C++では、記号の演算子に代わる単語形式の表現も用意されています。

論理ORの代替表現

if (a > 0 or b > 0) {
    // 処理
}

or|| と同じ意味です。

ビットORの代替表現

int result = a bitor b;

bitor| と同じ意味です。

複合代入の代替表現

flags or_eq Read;

or_eq|= と同じ意味です。

対応関係は次のとおりです。

記号代替表現
`
``
`=`

これらはマクロではなく、C++で正式に用意されている代替トークンです。

ただし、一般的なC++コードでは、||||= の記号表現のほうが広く使用されています。

OR演算子の優先順位

複数の演算子を組み合わせる場合は、演算子の優先順位に注意が必要です。

比較演算子と論理OR

次の式を考えます。

a == 1 || b == 2

比較演算子 == は、論理OR演算子 || よりも優先順位が高いため、次のように解釈されます。

(a == 1) || (b == 2)

論理ANDと論理OR

論理AND演算子 && は、論理OR演算子 || よりも優先順位が高くなっています。

a || b && c

この式は、次のように解釈されます。

a || (b && c)

次のようには解釈されません。

(a || b) && c

意図を明確にするため、複雑な条件式では括弧を付けることをおすすめします。

if (a || (b && c)) {
}
if ((a || b) && c) {
}

ビットORと論理AND

ビットOR演算子 | は、論理AND演算子 && よりも優先順位が高くなっています。

a | b && c

この式は次のように解釈されます。

(a | b) && c

ビット演算と論理演算を同じ式の中で使用すると、読み間違いが起こりやすくなります。

必要に応じて括弧を付けてください。

優先順位と評価順序は異なる

演算子の優先順位と、式が実行される順番は別の概念です。

優先順位

優先順位は、式がどのようにグループ化されるかを決めます。

a || b && c

これは、次のようにグループ化されます。

a || (b && c)

評価順序

評価順序は、どの式や関数が先に実行されるかを表します。

例えば、次のコードでは、| によって左右両方の関数が実行されます。

leftCheck() | rightCheck();

ただし、左右の関数の実行順序に依存するコードを書くべきではありません。

「どのように式がまとまるか」と「どの処理が先に実行されるか」は、分けて考える必要があります。

OR演算子でよくある間違い

条件判定で「|」を使ってしまう

次のコードでは、ビットOR演算子 | が使用されています。

if (isValid | checkData()) {
}

isValidcheckData() の結果が bool であれば、コンパイルできる場合があります。

ただし、| では必ず両方が評価されるため、isValidtrue でも checkData() が実行されます。

通常の条件判定では、論理OR演算子 || を使用します。

if (isValid || checkData()) {
}

比較対象を省略してしまう

次のコードは、「numberが1または2」という意味にはなりません。

if (number == 1 || 2) {
}

この式は、実質的に次のように解釈されます。

if ((number == 1) || 2) {
}

右側の数値2は0以外なので、true として扱われます。

そのため、この条件式は常に true になります。

正しくは、比較対象をそれぞれ記述します。

if (number == 1 || number == 2) {
}

文字の比較でも同じ間違いが起こる

誤った例は次のとおりです。

if (ch == 'a' || 'b') {
}

正しくは、次のように書きます。

if (ch == 'a' || ch == 'b') {
}

範囲外判定でANDを使ってしまう

1から10の範囲外であることを判定する場合は、ORを使用します。

if (value < 1 || value > 10) {
    std::cout << "範囲外です\n";
}

「1未満」または「10より大きい」のどちらかであれば、範囲外だからです。

範囲内であることを判定する場合は、ANDを使用します。

if (value >= 1 && value <= 10) {
    std::cout << "範囲内です\n";
}

std::bitsetでビットORの結果を確認する

ビットORの結果を2進数で確認したい場合は、std::bitset が便利です。

#include <bitset>
#include <iostream>

int main() {
    unsigned int a = 5;
    unsigned int b = 3;
    unsigned int result = a | b;

    std::cout << std::bitset<8>(a) << '\n';
    std::cout << std::bitset<8>(b) << '\n';
    std::cout << std::bitset<8>(result) << '\n';

    return 0;
}

出力結果は次のようになります。

00000101
00000011
00000111

上から順番に、5、3、ビットORの結果である7を表しています。

std::bitset を使用すると、どのビットが1になったのかを視覚的に確認できます。

enum classでビットORを使用する方法

enum class は、整数型へ暗黙的に変換されません。

そのため、通常はそのまま | を使用することはできません。

そのままでは使用できない例

enum class Permission {
    Read = 1,
    Write = 2
};

Permission permission =
    Permission::Read | Permission::Write;

このコードは、通常そのままではコンパイルできません。

operator|を定義する

enum class をフラグとして使用する場合は、operator| を定義できます。

#include <type_traits>

enum class Permission : unsigned int {
    None    = 0,
    Read    = 1u << 0,
    Write   = 1u << 1,
    Execute = 1u << 2
};

constexpr Permission operator|(
    Permission left,
    Permission right
) {
    using Underlying = std::underlying_type_t<Permission>;

    return static_cast<Permission>(
        static_cast<Underlying>(left) |
        static_cast<Underlying>(right)
    );
}

これにより、次のように記述できます。

Permission permissions =
    Permission::Read | Permission::Write;

operator|=も定義する

フラグを追加できるようにする場合は、operator|= も定義すると便利です。

constexpr Permission& operator|=(
    Permission& left,
    Permission right
) {
    left = left | right;
    return left;
}

次のように使用できます。

Permission permissions = Permission::None;

permissions |= Permission::Read;
permissions |= Permission::Write;

フラグを確認する関数

設定されているフラグを確認する関数も用意できます。

constexpr bool hasFlag(
    Permission value,
    Permission flag
) {
    using Underlying = std::underlying_type_t<Permission>;

    return (
        static_cast<Underlying>(value) &
        static_cast<Underlying>(flag)
    ) != 0;
}

使用例は次のとおりです。

if (hasFlag(permissions, Permission::Read)) {
    std::cout << "読み取り権限があります\n";
}

C++17以降のstd::byteとビットOR

C++17以降では、バイトデータを表す std::byte に対してもビットORを使用できます。

#include <cstddef>

std::byte a{0b00000001};
std::byte b{0b00000100};

std::byte result = a | b;

std::byte は数値計算よりも、純粋なバイトデータやビット列を扱う目的に適しています。

一般的な条件判定には使用しません。

論理ORとビットORの使い分け

複数の条件を判定する場合

if (isLoggedIn || isGuest) {
    // 処理
}

条件判定には、論理OR演算子 || を使用します。

複数のビットを結合する場合

unsigned int flags = Read | Write;

ビットの結合には、ビットOR演算子 | を使用します。

フラグを追加する場合

flags |= Execute;

既存の値へフラグを追加する場合は、複合代入演算子 |= を使用します。

C++のOR演算子に関するまとめ

C++のOR演算子には、主に論理OR演算子 || とビットOR演算子 | があります。

論理OR演算子 || は、複数の条件のうち、少なくとも1つが true であるかを判定するときに使用します。

if (conditionA || conditionB) {
    // どちらか一方がtrueなら実行される
}

組み込みの || では短絡評価が行われます。

左側が true の場合、右側の式は評価されません。

一方、ビットOR演算子 | は、整数の各ビットに対してOR演算を行います。

unsigned int flags = FlagA | FlagB;

ビットORでは短絡評価が行われず、左右の式が両方とも評価されます。

また、既存のフラグへ新しいフラグを追加するときは、|= を使用します。

flags |= FlagC;

特に注意したいのは、||| を混同しないことです。

通常の条件判定では ||、ビット操作では | を使用します。

また、次のように比較対象を省略してはいけません。

if (number == 1 || 2) {
}

正しくは、両方の条件を明示します。

if (number == 1 || number == 2) {
}

論理ORとビットORの違い、短絡評価、演算子の優先順位を理解することで、条件判定やフラグ管理を安全かつ正確に記述できるようになります。

以上、C++のOR演算子についてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次