Order of evaluation
표현식의 어떤 부분이든 평가되는 순서, 함수 인자의 평가 순서를 포함하여 지정되지 않습니다 (아래 나열된 일부 예외 제외). 컴파일러는 피연산자와 다른 하위 표현식을 어떤 순서로든 평가할 수 있으며, 동일한 표현식이 다시 평가될 때 다른 순서를 선택할 수 있습니다.
C++에는 왼쪽에서 오른쪽 또는 오른쪽에서 왼쪽 평가 개념이 존재하지 않습니다. 이는 연산자의 왼쪽에서 오른쪽 및 오른쪽에서 왼쪽 결합성과 혼동해서는 안 됩니다: 표현식 a ( ) + b ( ) + c ( ) 는 operator + 의 왼쪽에서 오른쪽 결합성으로 인해 ( a ( ) + b ( ) ) + c ( ) 로 파싱되지만, 런타임에 c ( ) 가 a ( ) 또는 b ( ) 보다 먼저, 나중에, 또는 사이에 평가될 수 있습니다:
가능한 출력:
b c a c a b
목차 |
"Sequenced before" 규칙 (since C++11)
표현식 평가
각 표현식의 평가는 다음을 포함합니다:
- 값 계산(Value computations) : 표현식이 반환하는 값의 계산. 여기에는 객체의 식별자 결정(glvalue 평가, 예: 표현식이 어떤 객체에 대한 참조를 반환하는 경우) 또는 이전에 객체에 할당된 값 읽기(prvalue 평가, 예: 표현식이 숫자나 다른 값을 반환하는 경우)가 포함될 수 있습니다.
- 부작용(side effects) 의 시작: volatile glvalue로 지정된 객체에 대한 접근(읽기 또는 쓰기), 객체 수정(쓰기), 라이브러리 I/O 함수 호출, 또는 이러한 작업을 수행하는 함수 호출.
순서
Sequenced before
는 동일한 스레드 내에서 평가
A
와
B
사이의 비대칭적, 추이적, 쌍대적 관계입니다.
-
A가B보다 먼저 시퀀싱되면(또는 동등하게B가A보다 나중에 시퀀싱되면 ),A의 평가는B의 평가가 시작되기 전에 완료됩니다. -
A가B보다 먼저 시퀀싱되지 않고B가A보다 먼저 시퀀싱되면,B의 평가는A의 평가가 시작되기 전에 완료됩니다. -
A가B보다 먼저 시퀀싱되지 않고B가A보다 먼저 시퀀싱되지 않으면 두 가지 가능성이 존재합니다:-
A와B의 평가가 비시퀀싱됨 : 어떤 순서로든 수행될 수 있으며 중첩될 수 있습니다(단일 실행 스레드 내에서 컴파일러는A와B를 구성하는 CPU 명령어들을 인터리브할 수 있습니다). -
A와 B의 평가가 불확정적으로 시퀀싱됨 : 어떤 순서로든 수행될 수 있지만 중첩될 수 없습니다:A가B보다 먼저 완료되거나,B가A보다 먼저 완료됩니다. 동일한 표현식이 다음에 평가될 때 순서가 반대일 수 있습니다.
-
표현식 X 가 표현식 Y 보다 먼저 순서화되었다(sequenced before) 고 말하는 것은, X 와 관련된 모든 값 계산 및 모든 부수 효과가 표현식 Y 와 관련된 모든 값 계산 및 모든 부수 효과보다 먼저 순서화된 경우를 말합니다.
규칙
- 모든 인수 표현식과 func 를 지정하는 후위 표현식
|
(C++26부터) |
- func 의 본문에 있는 모든 표현식 또는 문장
|
(C++26부터) |
| 규칙 10에는 한 가지 예외가 있습니다: std::execution::par_unseq 실행 정책 하에서 실행되는 표준 라이브러리 알고리즘에 의한 함수 호출은 unsequenced 상태이며 서로 임의로 인터리빙될 수 있습니다. | (C++17부터) |
|
13)
함수 호출 표현식에서 함수를 지칭하는 표현식은 모든 인수 표현식 및 모든 기본 인수보다 먼저 순서가 정해집니다.
14)
함수 호출에서 모든 매개변수의 초기화에 대한 값 계산 및 부수 효과는 다른 어떤 매개변수의 값 계산 및 부수 효과와 관련하여 비결정적으로 순서가 정해집니다.
15)
모든 오버로드된 연산자는 연산자 표기법을 사용하여 호출될 때 해당 연산자가 오버로드하는 내장 연산자의 순서 규칙을 따릅니다.
16)
첨자 표현식
E1
[
E2
]
에서,
E1
은
E2
보다 먼저 순서가 정해집니다.
17)
멤버 포인터 표현식
E1.
*
E2
또는
E1
-
>
*
E2
에서,
E1
은
E2
보다 먼저 순서가 정해집니다
(
E1
의 동적 타입이
E2
이 참조하는 멤버를 포함하지 않는 경우는 제외).
18)
시프트 연산자 표현식
E1
<<
E2
및
E1
>>
E2
에서,
E1
은
E2
보다 먼저 순서가 정해집니다.
19)
모든 단순 대입 표현식
E1
=
E2
및 모든 복합 대입 표현식
E1 @
=
E2
에서,
E2
는
E1
보다 먼저 순서가 정해집니다.
20)
괄호로 묶은 초기화자에서 쉼표로 구분된 표현식 목록의 모든 표현식은 함수 호출과 같이 평가됩니다(비결정적 순서).
|
(C++17부터) |
정의되지 않은 동작
동작은 다음과 같은 경우에 undefined 입니다:
i = ++i + 2; // well-defined i = i++ + 2; // undefined behavior until C++17 f(i = -2, i = -2); // undefined behavior until C++17 f(++i, ++i); // undefined behavior until C++17, unspecified after C++17 i = ++i + i++; // undefined behavior
cout << i << i++; // undefined behavior until C++17 a[i] = i++; // undefined behavior until C++17 n = ++i + i; // undefined behavior
- 동일한 메모리 위치에 대한 부작용(side effect)
- 동일한 메모리 위치에 있는 임의의 객체 값을 사용한 값 계산(value computation)
- 해당 메모리 위치와 겹치는 저장 공간을 차지하는 객체의 lifetime 시작 또는 종료
union U { int x, y; } u; (u.x = 1, 0) + (u.y = 2, 0); // undefined behavior
시퀀스 포인트 규칙 (C++11까지)
C++11 이전 정의
표현식의 평가는 부수 효과를 발생시킬 수 있으며, 이는 다음과 같습니다: volatile 좌측값으로 지정된 객체에 접근하기, 객체 수정하기, 라이브러리 I/O 함수 호출하기, 또는 이러한 연산 중 어떤 것을 수행하는 함수 호출하기.
시퀀스 포인트 는 실행 시퀀스 상의 한 지점으로, 시퀀스 내 이전 평가들의 모든 부수 효과가 완료되고, 이후 평가들의 부수 효과는 아직 시작되지 않은 상태를 말합니다.
C++11 이전 규칙
a && b a || b a ? b : c a , b
C++11 이전의 정의되지 않은 동작
동작은 다음과 같은 경우에 undefined 입니다:
i = ++i + i++; // undefined behavior i = i++ + 1; // undefined behavior i = ++i + 1; // undefined behavior ++ ++i; // undefined behavior f(++i, ++i); // undefined behavior f(i = -1, i = -1); // undefined behavior
cout << i << i++; // undefined behavior a[i] = i++; // undefined behavior
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 1885 | C++11 | 함수 반환 시 자동 변수의 소멸 순서가 명시적이지 않았음 | 순서 규칙 추가됨 |
| CWG 1949 | C++11 | C++ 표준에서 "sequenced after"가 사용되었지만 정의되지 않았음 | "sequenced before"의 역으로 정의됨 |
| CWG 1953 | C++11 |
메모리 위치와 관련된 부수 효과 및 값 계산이 동일 메모리 위치에서
객체의 수명 시작/종료와 비순서 관계를 가질 수 있었음 |
이 경우 동작이
정의되지 않음 |
| CWG 2146 | C++98 | 정의되지 않은 동작을 포함하는 경우들이 비트 필드를 고려하지 않았음 | 고려됨 |
참고문헌
- C++23 표준 (ISO/IEC 14882:2024):
-
- 6.9.1 프로그램 실행 [intro.execution]
-
- 7.6.1.6 증가 및 감소 연산자 [expr.post.incr]
-
- 7.6.2.8 new 연산자 [expr.new]
-
- 7.6.14 논리 AND 연산자 [expr.log.and]
-
- 7.6.15 논리 OR 연산자 [expr.log.or]
-
- 7.6.16 조건 연산자 [expr.cond]
-
- 7.6.19 대입 및 복합 대입 연산자 [expr.ass]
-
- 7.6.20 쉼표 연산자 [expr.comma]
-
- 9.4.5 목록 초기화 [dcl.init.list]
- C++20 표준(ISO/IEC 14882:2020):
-
- 6.9.1 프로그램 실행 [intro.execution]
-
- 7.6.1.5 증가 및 감소 연산자 [expr.post.incr]
-
- 7.6.2.7 new 연산자 [expr.new]
-
- 7.6.14 논리 AND 연산자 [expr.log.and]
-
- 7.6.15 논리 OR 연산자 [expr.log.or]
-
- 7.6.16 조건 연산자 [expr.cond]
-
- 7.6.19 대입 및 복합 대입 연산자 [expr.ass]
-
- 7.6.20 쉼표 연산자 [expr.comma]
-
- 9.4.4 목록 초기화 [dcl.init.list]
- C++17 표준(ISO/IEC 14882:2017):
-
- 4.6 프로그램 실행 [intro.execution]
-
- 8.2.6 증가 및 감소 연산자 [expr.post.incr]
-
- 8.3.4 new 연산자 [expr.new]
-
- 8.14 논리 AND 연산자 [expr.log.and]
-
- 8.15 논리 OR 연산자 [expr.log.or]
-
- 8.16 조건 연산자 [expr.cond]
-
- 8.18 대입 및 복합 대입 연산자 [expr.ass]
-
- 8.19 쉼표 연산자 [expr.comma]
-
- 11.6.4 목록 초기화 [dcl.init.list]
- C++14 표준(ISO/IEC 14882:2014):
-
- 1.9 프로그램 실행 [intro.execution]
-
- 5.2.6 증가 및 감소 연산자 [expr.post.incr]
-
- 5.3.4 new 연산자 [expr.new]
-
- 5.14 논리 AND 연산자 [expr.log.and]
-
- 5.15 논리 OR 연산자 [expr.log.or]
-
- 5.16 조건 연산자 [expr.cond]
-
- 5.17 대입 및 복합 대입 연산자 [expr.ass]
-
- 5.18 쉼표 연산자 [expr.comma]
-
- 8.5.4 목록 초기화 [dcl.init.list]
- C++11 표준 (ISO/IEC 14882:2011):
-
- 1.9 프로그램 실행 [intro.execution]
-
- 5.2.6 증가 및 감소 연산자 [expr.post.incr]
-
- 5.3.4 new 연산자 [expr.new]
-
- 5.14 논리 AND 연산자 [expr.log.and]
-
- 5.15 논리 OR 연산자 [expr.log.or]
-
- 5.16 조건 연산자 [expr.cond]
-
- 5.17 대입 및 복합 대입 연산자 [expr.ass]
-
- 5.18 쉼표 연산자 [expr.comma]
-
- 8.5.4 목록 초기화 [dcl.init.list]
참고 항목
- 연산자 우선순위 소스 코드 표현으로부터 표현식이 어떻게 구성되는지 정의합니다.
|
C 문서
참조:
평가 순서
|