Short Circuit Evaluation 개념 및 주의점 (단축 평가)

AND(&&) 연산자 혹은 OR(||) 연산자와 같은 논리 연산자, Logical Operator를 사용할 때, 굳이 모든 연산을 다하지 않더라도 결과를 알게 되는 경우가 있습니다.

하나라도 False를 만족하는 AND 연산자하나라도 True를 만족하는 OR 연산자의 경우입니다.

이처럼 앞 부분의 결과만으로 조건을 판단할 수 있을 때 뒷 부분은 생략하는 것을 Short Circuit Evaluation, 단락(단축) 평가라 합니다.

1
2
3
4
5
if(conditionA && conditionB)
// conditionA가 False인 경우 conditionB 검사없이 넘어감

if(conditionA || conditionB)
// conditionA가 True인 경우 conditionB 검사없이 넘어감

Short Circuit Evaluation은 아래와 같이 응용할 수 있습니다.

1
if (myStack.empty() || myStack.top() != num)

만약 myStack이 empty일때 top을 호출하게 되면 런타임 에러가 발생하게 됩니다.
하지만 위와 같이 empty를 먼저 검사하게되면 Short Circuit Evaluation에 의해 empty일 경우 top이 호출되지 않기 때문에 에러를 막을 수 있습니다.

사용시 주의점

앞 부분으로만 결과가 판단되는 경우에 뒷 부분의 구문은 실행되지 않습니다.

아래 예시 코드를 봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream> 

bool LeftCondition(bool b) {
std::cout << "Left Condition Executed " << b << std::endl;
return b;
}

bool RightCondition(bool b) {
std::cout << "Right Condition Executed " << b << std::endl;
return b;
}

int main(void) {
std::cout << "# Case A (false && true) " << std::endl;
if (LeftCondition(false) && RightCondition(true)) {
// Left
}

std::cout << "# Case B (true && true) " << std::endl;
if (LeftCondition(true) && RightCondition(true)) {
// Left, Right
}

std::cout << "# Case C (false || true) " << std::endl;
if (LeftCondition(false) || RightCondition(true)) {
// Left, Right
}

std::cout << "# Case D (true || true) " << std::endl;
if (LeftCondition(true) || RightCondition(true)) {
// Left
}
}

Case A와 D같은 경우 Short Circuit Evaluation에 의해 Left 조건만 실행됩니다.
다시 말해, Right 함수가 호출이 되지 않을 때가 있습니다.
만약 Right 함수이 꼭 호출되어야 한다면 위와 같이 작성할 경우 문제가 되고, 원인 파악이 어렵습니다.
따라서 앞의 조건과 뒤의 조건을 모두 호출하려는 경우엔 코드를 다른 방식으로 작성하여야 합니다.

ref) https://en.wikipedia.org/wiki/Short-circuit_evaluation

Namespace

네임스페이스는 코드에서 이름이 서로 충돌하는 문제를 해결하기 위해 나온 개념입니다. 예를 들어 foo라는 이름의 함수를 만들어서 사용하다가 외부 라이브러리를 참조했을 때, 거기에도 foo라는 함수가 있다면 충돌이 발생합니다. 이럴 경우, 컴파일러는 foo가 어떤 함수를 가리키는지 알 수 없게 됩니다.

아래와 같은 간단한 예제 코드를 살펴봅시다.

[namespace 예제 코드]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

namespace nameA {
void foo() {
std::cout << "It is in nameA" << std::endl;
}
};

namespace nameB {
void foo() {
std::cout << "It is in nameB" << std::endl;
}
};

int main(void) {
nameA::foo();
nameB::foo();

return 0;
}

[실행 결과]

1
2
It is in nameA
It is in nameB

🎊 Scope Resolution Operator인 :: 기호를 이용해서 사용하면 됩니다.

nested namespace라고 해서 namespace를 중첩해서 사용하는 것도 가능합니다. namespace alias로 이름을 다르게 표현할 수 있는데 아래 예제를 통해 두가지를 한번에 확인해봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>

namespace A {
namespace B {
namespace C {
void foo() {
std::cout << "Nested namespace example" << std::endl;
}
}
}
}

namespace a::b::c {
void foo() {
std::cout << "Supported C++17" << std::endl;
}
}

int main(void) {
namespace ABC = A::B::C;
namespace abc = a::b::c;

A::B::C::foo();
a::b::c::foo();
ABC::foo();
abc::foo();

return 0;
}
1
2
3
4
Nested namespace example
Supported C++17
Nested namespace example
Supported C++17

🎊 만약 사용하는 빈도수가 높은 경우 using 이라는 지시자를 통해 컴파일러에 처리하도록 하면 됩니다. 보통 c++ 코드에서 쓰이는 using namespace std; 도 std라는 namespace를 :: 기호 없이 사용하겠다는 뜻으로 쓰인 구문입니다.

[using 예제 코드]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>

using std::cout;

namespace nameA {
void foo() {
cout << "It is in nameA" << std::endl;
}
};

namespace nameB {
void foo() {
cout << "It is in nameB" << std::endl;
}
};

using namespace nameA;

int main(void) {
foo();
nameB::foo();

cout << "using std::cout (not endl)" << std::endl;

return 0;
}
1
2
3
It is in nameA
It is in nameB
using std::cout (not endl)

using namespace를 통해 nameA:: 를 생략하여 사용하였습니다. (using을 했어도 nameA를 붙여도 무방합니다.) 또한, using namespace가 아니라 using 후 특정 항목만 지정해서 사용가능합니다. using std::cout 을 통해 컴파일러에게 지시를 하여 cout은 std::없이 사용했지만 endl의 경우 std::endl로 사용하였습니다.

🧨 웬만하면 using 지시자는 사용하지 않는 방향으로 개발하시는 것이 좋습니다. 예상치 못한 변수나 함수명이 겹치는 경우가 발생할 수 있기 때문입니다.

ref) https://en.cppreference.com/w/cpp/language/namespace