#cpp Short-circuit evaluation - 결과 뻔한데 뒤에 인자까지 평가할 필요 없지 뭐

1 minute read

Object* object = NULL;
if (object && object->IsValid())
{
    // ...
}

C++에서 다음과 같은 코드가 있을 때, 죽을까 안 죽을까? 정답은 안 죽는다 Short-circuit evaluation 때문에 그런데, 이 용어가 생각이 안 나서 대학교 때 배운 Programming Language 책에서 용어를 찾았다. 그리고 기념으로다 포스팅.

Short-circuit evaluation을 간단히 설명하면 and, or 논리 연산에서 인자 하나만 보고 결과를 확실히 알 수 있을 때, 뒤에 나오는 인자를 확인하지 않고 바로 답을 내는 방법이다. && 연산일 때, 하나라도 false이면 무조건 답이 false이고 || 연산일 땐, 하나라도 true이면 무조건 답이 true이다. C++뿐만 아니라 C#, Java도 Short-circuit evaluation을 지원한다. 지원하는 언어를 보려면 여기를 클릭.

Object* object = new Object();
if (object->operator1() && object->operator2())
{
    // ...
}

주의해야 할 것은 Short-circuit evaluation 때문에 side-effect가 발생할 수 있다는 것이다. 위 예제 같은 경우 만약 operator1()이 true를 리턴하면 operator2()가 실행되고 operator1()이 false를 리턴하면 operator2()는 실행조차 안 된다. 위에 두 멤버 함수가 command 함수가 아닌 query 함수라면 별문제가 없겠지만 이게 command(오브젝트 상태를 변경하는 멤버함수)라면 side-effect가 짠 하고 나타난다. 생긴 모양 보니 결함을 좀 찾기 어려울 듯.

Object* object = new Object();
const bool retOperator1 = object->operator1();
const bool retOperator2 = object->operator2();

if (retOperator1 && retOperator2)
{
    // ...
}

query 함수로 바꾸는 게 올바른 리펙토링 방향이지만 시간이 없다면 이렇게 수정하면 된다. 결과 값에 상관없이 두 멤버 함수 모두다 호출된다. 그리고 변수 이름도 알아먹기 좋게 한다면 가독성도 좋아진다. 아마 이렇게 수정하면 이후에 리펙토링을 안 하겠지?

#define assert_if(con, exp) assert(!(con) || (exp))

참고로 Short-circuit evaluation을 이용해 assert_if 매크로를 이렇게 구현할 수도 있다.

무의식적으로 사용하고 있는데, Short-circuit evaluation을 제대로 정리해둬야 위험한 코드를 만들지 않는다.