← 문제로

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.

단계별 내부 동작

  1. f(g(), h())g()h() 는 함수 인자라 indeterminately sequenced. 순서 미지정 → 출력 "gh" 또는 "hg". C++17 도 인자 간 순서는 미지정. 단 한 인자 평가는 다음 인자 시작 전에 끝난다(비-인터리브, C++11+). UB 아님.
  2. 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과 같은 규칙으로 구제된다.)
  3. a[k] = k++;C++17 이전엔 UB(첨자에 쓰인 k 읽기와 k++ 가 unsequenced). C++17 부터 우변이 좌변보다 sequenced-beforek++(우변)이 0 산출·k=1, 좌변 a[k] 는 k=1 사용, 대입 → a[1] = 0. 잘 정의. (2)와 함께 C++17 이 구제한 대표 케이스.
  4. std::cout << j << j++;operator<<( operator<<(cout, j), j++ ) 로, operator<<(cout,j)(왼쪽 인자, j 읽기)와 j++(오른쪽 인자, j 쓰기)는 함수 인자라 indeterminately sequenced. 서로 unsequenced 가 아니므로 UB 는 아니고 결과가 미지정(어느 인자가 먼저 평가되냐에 따라 출력이 달라짐). C++17 도 함수 인자 순서는 미지정이라 컴파일러마다 다르게 출력될 수 있다 → 이런 코드는 금지.
  5. 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 피연산자).
  • 실무 규칙: 한 식에서 같은 변수의 수정+사용을 섞지 말고 순서 의존 코드를 쓰지 말 것.