Flat Leon Works

アプリやゲームを作ってます。

【C++】短絡評価(ショートサーキット)を知ろう

短絡評価とは

短絡評価とは論理演算子&&||で行われる、式の評価の省略のことです。ショートサーキットとも呼ばれます。

短絡評価は具体的には以下のようなルールで行われます。

  • ||演算では左側の式が、だと判明した時点で、右側の式の評価が省略されます。
  • &&演算では左側の式が、だと判明した時点で、右側の式の評価が省略されます。

短絡評価の例

#include <stdio.h>
bool DevilFunction( void )
{
    printf( "I am a devil." );
    int* array = new int[1];
    array[27] = 666;
    return array[1234];
}

int main()
{
    true || DevilFunction();
    false && DevilFunction();
    return 0;
}

関数DevilFunctionはnewしっぱなしだったり、newした領域外に値を書き込んだりと悪いことする関数です。true || DevilFunction();でDevilFunctionを呼んでいるように見えますが、||の短絡評価により、DevilFunction()の式の評価は省略されます。つまりDevilFunction関数は呼ばれていません。同じようにfalse && DevilFunction();も短絡評価によりDevilFunction関数が呼ばれなくなっています。

なぜ短絡評価が存在するのか

なぜ短絡評価が存在するのかというと、||演算の場合、一方がであると判明した時点で、もう一方がどんな値だろうと||演算式の結果はであると確定するからだと思います。&&演算の場合も同様です。つまり無駄な処理を省いているわけです。

短絡評価の使い道

短絡評価は式の評価を省略することができるので、これを利用したちょっとしたテクニックもあります。

ポインタと合わせて使う

ポインタは非NULLかどうかで真偽値になるので、このような書き方をすることができます。

ptr && ptr->IsActive();

この場合ptrがNULLでない場合のみIsActiveメンバ関数が呼ばれるようになります。このとき呼び出すメンバ関数の戻り値はbool(またはboolへ変換できる型)である必要があります。

軽い処理を先に持ってくる

||演算や&&演算の中で軽い処理と重い処理がある場合、先に(左側に)軽い処理を持ってくることで、重い処理をなるべく行わないようにすることができます。

lightWork() && heavyWork();

ただし、lightWork関数がほとんどの場合で真を返すときは、ほとんどの場合で省略されないことになるのであまり意味がありません。

短絡評価の注意点

短絡評価のまさに式の評価が省略される点について注意が必要です。たとえば、その省略された式の中で何か「効果のある処理」つまり「副作用のある処理」を行っていた場合、その効果が発生しなくなります。

#include <stdio.h>
#include <string.h>
#include <string>

std::string str( "banana orange" );

// 削除に成功したらtrueを返す
bool Erase( const char* p_findStr )
{
    int pos = str.find( p_findStr );
    if ( pos == -1 ){ return false; }
    str.replace( pos, strlen( p_findStr ), "" );
    return true;
}

int main()
{
    // appleやbananaが存在する場合削除
    // そして、両方とも存在していた場合、メッセージを表示
    if ( Erase( "apple" ) && Erase( "banana" ) )
    {
        printf( "There is no apple and banana.\n" );
    }
    printf( "%s\n", str.c_str() );
    return 0;
}

このプログラムは、strに"apple"や"banana"が存在する場合、削除を行います。そして両方とも削除した場合にメッセージを表示します。strは"banana orange"なので、"banana"だけ削除され、最終的にstrは" orange"になることを想定しています。

しかし、実際にはstrは"banana orange"のままになってしまいます。

これは左側のReplace( "apple", "" )で"apple"の削除が行われなかったため、式が偽となり、短絡評価が行われ右側のReplace( "banana", "" )による"banana"の削除が行われなくなってしまったのが原因です。つまり"apple"が存在しないと、"banana"の削除が行われないようになっていたのです。

"apple"の削除、"banana"の削除を両方とも確実に行わせるには以下のようにするべきでしょう。

bool erasedApple = Erase( "apple" );
bool erasedBanana = Erase( "banana" );
if ( erasedApple && erasedBanana )
{
    printf( "There is no apple and banana.\n" );
}

まとめ

短絡評価は||,&&演算子を使う上で必須の知識です。覚えておきましょう。