40. 평가 순서(order of evaluation)와 시퀀싱, C++17의 규칙 변화
난이도 중내 리뷰 · C++
해설 · C++
해설 — 평가 순서(order of evaluation)와 시퀀싱, C++17의 규칙 변화
난이도: 중상
요약
부분식의 평가 순서는 대부분 미지정(unspecified) 이다. UB(미정의) 는 같은 스칼라에 대한 두 접근(하나 이상이 쓰기)이 서로 unsequenced 일 때만 발생한다. C++11 부터 함수 인자들은 서로 indeterminately sequenced(겹치지 않되 순서는 미지정)이고, C++17 은 대입·시프트·멤버 접근 등에 새 시퀀싱을 추가해 과거 UB 였던 몇몇 식을 잘 정의되게 만들었다. 단 함수 인자 간 순서는 여전히 미지정이다.
핵심 개념
- sequenced-before: A 가 B 보다 확실히 먼저. unsequenced: 순서 없음(겹칠 수 있음) → 같은 객체 접근이 섞이면 UB. indeterminately sequenced: 둘 중 하나가 먼저지만 어느 쪽인지 미지정(겹치지 않음) → UB 아님, 결과만 미지정.
- UB 조건: 한 스칼라에 대한 (부수효과, 부수효과) 또는 (부수효과, 값계산)이 unsequenced.
단계별 내부 동작
- f(g(), h()) —
g()와h()는 함수 인자라 indeterminately sequenced. 순서 미지정 → 출력 "gh" 또는 "hg". C++17 도 인자 간 순서는 미지정. 단 한 인자 평가는 다음 인자 시작 전에 끝난다(비-인터리브, C++11+). UB 아님. - i = i++ + 1; — C++17 이전엔 UB(대입의 쓰기와
i++부수효과가 unsequenced). C++17 부터 대입E1 = E2에서 우변 E2 가 좌변 E1 보다 sequenced-before, 그리고 대입은 두 피연산자의 값계산 뒤에 시퀀싱된다. 따라서i++(우변)의 부수효과가 대입 store 보다 먼저 → 더 이상 unsequenced 가 아니라 잘 정의:i++이 0 산출·i=1, 우변=0+1=1, 그 뒤i=1→ 최종 i == 1. (항목 3과 같은 규칙으로 구제된다.) - a[k] = k++; — C++17 이전엔 UB(첨자에 쓰인
k읽기와k++가 unsequenced). C++17 부터 우변이 좌변보다 sequenced-before →k++(우변)이 0 산출·k=1, 좌변a[k]는 k=1 사용, 대입 →a[1] = 0. 잘 정의. (2)와 함께 C++17 이 구제한 대표 케이스. - std::cout << j << j++; —
operator<<( operator<<(cout, j), j++ )로,operator<<(cout,j)(왼쪽 인자,j읽기)와j++(오른쪽 인자,j쓰기)는 함수 인자라 indeterminately sequenced. 서로 unsequenced 가 아니므로 UB 는 아니고 결과가 미지정(어느 인자가 먼저 평가되냐에 따라 출력이 달라짐). C++17 도 함수 인자 순서는 미지정이라 컴파일러마다 다르게 출력될 수 있다 → 이런 코드는 금지. - b.set(x++).set(x++); — C++17 부터
e1.f(a).g(b)형태에서 객체 표현식e1.f(a)전체가g(b)의 인자b평가보다 sequenced-before. 따라서 첫x++이 둘째x++보다 먼저 평가됨이 보장(체이닝 순서 확정). 각set인자 하나만x를 건드려 UB 없음. C++14 까지는 이 순서가 보장되지 않았다.
흔한 오해·함정
- "C++17 이 평가 순서를 다 정했다" → 아니다. 함수 인자 간 순서는 여전히 미지정.
구제된 것은 대입(우변→좌변)·시프트·
[]·./->/.*등 특정 연산자의 순서. - 여전히 UB 인 예:
int x = i++ + i++;—+의 두 피연산자는 서로 unsequenced 라 같은i에 대한 두 부수효과가 겹쳐 C++17 에서도 UB. 또f(i, i++)는 인자가 indeterminately sequenced 라 UB 는 아니고 미지정. - unspecified(값은 임의지만 프로그램 유효) vs undefined(프로그램 전체 무효)의 구분.
면접 포인트
- unsequenced(UB) vs indeterminately sequenced(미지정, UB 아님) vs sequenced-before.
- C++17 이 구제한 것(
i=i++ +1,a[i]=i++등 대입 우변→좌변)과 못 한 것(함수 인자 순서,i++ + i++같은 unsequenced 피연산자). - 실무 규칙: 한 식에서 같은 변수의 수정+사용을 섞지 말고 순서 의존 코드를 쓰지 말 것.