programing

초기화에 lvalue에서 rvalue로 변환이 수반됩니까?

firstcheck 2021. 1. 16. 09:53
반응형

초기화에 lvalue에서 rvalue로 변환이 수반됩니까? `int x = x;`UB입니까?


C ++ 표준에는 3.3.2, "Point of declaration"의 "놀라운"이름 조회의 유명한 예가 포함되어 있습니다.

int x = x;

이것은 x(기본 유형 이기 때문에 ) 자체적으로 초기화 되며, 따라서 초기화되지 않은 값을 갖습니다 (자동 변수라고 가정).

이것은 실제로 정의되지 않은 동작입니까?

4.1 "Lvalue에서 rvalue로 변환"에 따르면 초기화되지 않은 값에서 lvalue에서 rvalue로 변환을 수행하는 것은 정의되지 않은 동작입니다. 오른손 x이이 변환을 겪습니까? 그렇다면 예제에 실제로 정의되지 않은 동작이 있습니까?


업데이트 : 의견에 대한 토론에 이어이 답변 끝에 몇 가지 증거를 추가했습니다.


면책 조항 : 나는이 답변이 다소 추측임을 인정합니다. 반면에 C ++ 11 표준의 현재 공식화는보다 공식적인 답변을 허용하지 않는 것 같습니다.


이 Q & A 의 맥락 에서 C ++ 11 표준 은 각 언어 구조에서 예상되는 값 범주 를 공식적으로 지정하지 못하는 것으로 나타났습니다 . 이어지는 질문은 이니셜 라이저 에 대한 질문이지만 대부분 내장 연산자 에 초점을 맞출 것 입니다. 결국 연산자의 경우에 대해 그린 결론을 이니셜 라이저의 경우로 확장하게됩니다.

기본 제공 연산자의 경우 공식 사양이 없음에도 불구하고 표준에서 의도 된 사양이 값이 필요한 곳과 지정되지 않은 경우 prvalue를 예상 할 수 있다는 증거가 있습니다. 그렇지 않으면 .

예를 들어 3.10 / 1 절의 메모는 다음과 같습니다.

5 절의 각 내장 연산자에 대한 설명은 산출되는 값의 범주와 예상되는 피연산자의 값 범주를 나타냅니다. 예를 들어, 내장 할당 연산자는 왼쪽 피연산자가 lvalue이고 오른쪽 피연산자가 prvalue이고 결과로 lvalue를 산출한다고 예상합니다. 사용자 정의 연산자는 함수이며, 예상하고 산출하는 값의 범주는 매개 변수 및 반환 유형에 의해 결정됩니다.

반면에 할당 연산자에 대한 섹션 5.17에서는 이에 대해 언급하지 않습니다. 그러나 lvalue에서 rvalue 로의 변환을 수행 할 가능성이 다시 언급되어 있습니다 (문단 5.17 / 1).

따라서 함수 호출 은 lvalue에서 rvalue 로의 변환 과 단일 복합 할당 연산자와 관련된 부작용 사이 개입해서는 안됩니다.

물론 rvalue가 예상되지 않았다면이 메모는 의미가 없습니다.

링크 된 Q & A에 대한 의견에서 Johannes Schaub지적한 또 다른 증거는 4/8에서 발견되었습니다 .

특정 변환이 억제되는 컨텍스트가 있습니다. 예를 들어, lvalue에서 rvalue 로의 변환은 단항 & 연산자의 피연산자에서 수행되지 않습니다. 해당 연산자 및 컨텍스트에 대한 설명에 특정 예외가 제공됩니다.

이것은 lvalue에서 rvalue 로의 변환이 달리 지정되는 경우를 제외하고 내장 연산자의 모든 피연산자에서 수행됨을 의미합니다. 즉, 달리 지정하지 않는 한 rvalue는 기본 제공 연산자의 피연산자로 예상됩니다.


어림짐작:

초기화가 할당이 아니므로 운영자가 논의에 참여하지 않더라도 사양의이 영역이 위에서 설명한 것과 동일한 문제의 영향을받는 것으로 의심됩니다.

이 신념을 뒷받침하는 추적은 참조 초기화 (lvalue 이니셜 라이저 표현식의 값이 필요하지 않음)에 대한 단락 8.5.2 / 5에서도 찾을 수 있습니다 .

통상 lvalues에 대한 직접적인 바인딩을 완료 할 때 좌변 투 r- 수치 (4.1) 어레이 투 포인터 (4.2), 및 함수에 대한 포인터는 (4.3) 표준 전환 따라서 필요하지되고 억제된다.

"보통"이라는 단어는 참조 유형이 아닌 객체를 초기화 할 때 lvalue에서 rvalue 로의 변환이 적용된다는 것을 의미하는 것 같습니다.

따라서 이니셜 라이저의 예상 값 범주에 대한 요구 사항이 잘못 지정되어 있지만 (완전히 누락되지 않은 경우) 의도 된 사양이 다음과 같다고 가정하는 것이 합리적이라는 증거를 근거로합니다 .

언어 구조에 값이 필요한 경우에는 달리 지정하지 않는 한 prvalue가 예상됩니다 .

이 가정 하에서 예제에서 lvalue에서 rvalue 로의 변환이 필요하며 이는 정의되지 않은 동작으로 이어집니다.


추가 증거 :

이 추측을 뒷받침하는 추가 증거를 제공 하기 위해 복사 초기화에 lvalue에서 rvalue 로의 변환이 실제로 필요하지 않도록 잘못 가정 하고 다음 코드를 고려하십시오 ( 기여해 주신 jogojapan 에게 감사드립니다 ).

int y;
int x = y; // No UB
short t;
int u = t; // UB! (Do not like this non-uniformity, but could accept it)
int z;
z = x; // No UB (x is not uninitialized)
z = y; // UB! (Assuming assignment operators expect a prvalue, see above)
       // This would be very counterintuitive, since x == y

이 불균일 한 행동은 나에게 많은 의미가 없습니다. IMO가 더 의미있는 것은 값이 필요한 곳마다 prvalue가 예상된다는 것입니다.

또한 Jesse Good 이 그의 답변에서 올바르게 지적했듯이 C ++ 표준의 핵심 단락은 8.5 / 16입니다.

— 그렇지 않으면 초기화되는 객체의 초기 값은 초기화 표현식(변환 가능) 값입니다 . 필요하다면 표준 변환 (Clause 4)을 사용 하여 이니셜 라이저 표현식을 대상 유형 의 cv-unqualified 버전으로 변환합니다 . 사용자 정의 변환은 고려되지 않습니다. 변환을 수행 할 수 없으면 초기화가 잘못된 것입니다. [참고 : "cv1 T"유형의 표현식은 cv 한정자 cv1 및 cv2와 관계없이 "cv2 T"유형의 객체를 초기화 할 수 있습니다.

그러나 Jesse는 주로 " 필요한 경우 "비트 에 초점을 맞추지 만 " 유형 " 이라는 단어도 강조하고 싶습니다 . 위의 단락은 표준 변환이 " 필요한 경우 "대상 유형 으로 변환하는 데 사용된다는 것을 언급 하지만 카테고리 변환 에 대해서는 언급하지 않습니다 .

  1. 필요한 경우 카테고리 변환이 수행됩니까?
  2. 필요합니까?

두 번째 질문에 관한 내용은 답변의 원래 부분에서 논의했듯이 C ++ 11 표준은 현재 범주 변환이 필요한지 여부를 지정하지 않습니다. 복사 초기화가 초기화 프로그램으로 prvalue를 예상하는지 여부는 어디에도 언급되어 있지 않기 때문입니다. . 따라서 명확한 대답은 불가능합니다. 그러나 나는 이것이 의도 된 사양 이라고 가정하기에 충분한 증거를 제공했다고 생각 하므로 대답은 "예"가 될 것입니다.

첫 번째 질문에 대해서도 대답이 "예"라고 생각합니다. "아니오"이면 분명히 올바른 프로그램의 형식이 잘못되었습니다.

int y = 0;
int x = y; // y is lvalue, prvalue expected (assuming the conjecture is correct)

요약하면 (A1 = " 질문 1에 대한 답변 ", A2 = " 질문 2에 대한 답변 ") :

          | A2 = Yes   | A2 = No |
 ---------|------------|---------|
 A1 = Yes |     UB     |  No UB  | 
 A1 = No  | ill-formed |  No UB  |
 ---------------------------------

A2가 "아니오"이면 A1은 중요하지 않습니다. UB가 없지만 첫 번째 예제의 기괴한 상황 (예 : z = yUB를 제공하지만 UB를 제공하는 z = x경우 x == y)이 나타납니다. 반면에 A2가 "예"이면 A1이 중요해집니다. 그러나 "예"라는 것을 증명할 충분한 증거가 제공되었습니다.

따라서 내 논문은 A1 = "예"이고 A2 = "예"이며 정의되지 않은 동작을 가져야합니다 .


추가 증거 :

결함 보고서 ( Jesse Good 제공 )는이 경우 정의되지 않은 동작을 제공하기위한 변경을 제안합니다.

[...] In addition, 4.1 [conv.lval] paragraph 1 says that applying the lvalue-to-rvalue conversion to an “object [that] is uninitialized” results in undefined behavior; this should be rephrased in terms of an object with an indeterminate value.

In particular, the proposed wording for Paragraph 4.1 says:

When an lvalue-to-rvalue conversion occurs in an unevaluated operand or a subexpression thereof (Clause 5 [expr]) the value contained in the referenced object is not accessed. In all other cases, the result of the conversion is determined according to the following rules:

— If T is (possibly cv-qualified) std::nullptr_t, the result is a null pointer constant (4.10 [conv.ptr]).

— Otherwise, if the glvalue T has a class type, the conversion copy-initializes a temporary of type T from the glvalue and the result of the conversion is a prvalue for the temporary.

— Otherwise, if the object to which the glvalue refers contains an invalid pointer value (3.7.4.2 [basic.stc.dynamic.deallocation], 3.7.4.3 [basic.stc.dynamic.safety]), the behavior is implementation-defined.

— Otherwise, if T is a (possibly cv-qualified) unsigned character type (3.9.1 [basic.fundamental]), and the object to which the glvalue refers contains an indeterminate value (5.3.4 [expr.new], 8.5 [dcl.init], 12.6.2 [class.base.init]), and that object does not have automatic storage duration or the glvalue was the operand of a unary & operator or it was bound to a reference, the result is an unspecified value. [Footnote: The value may be different each time the lvalue-to-rvalue conversion is applied to the object. An unsigned char object with indeterminate value allocated to a register might trap. —end footnote]

Otherwise, if the object to which the glvalue refers contains an indeterminate value, the behavior is undefined.

— Otherwise, if the glvalue has (possibly cv-qualified) type std::nullptr_t, the prvalue result is a null pointer constant (4.10 [conv.ptr]). Otherwise, the value contained in the object indicated by the glvalue is the prvalue result.


An implicit conversion sequence of an expression e to type T is defined as being equivalent to the following declaration, using t as the result of the conversion (modulo value category, which will be defined depending on T), 4p3 and 4p6

T t = e;

The effect of any implicit conversion is the same as performing the corresponding declaration and initialization and then using the temporary variable as the result of the conversion.

In clause 4, the conversion of an expression to a type always yields expressions with a specific property. For example, conversion of 0 to int* yields a null pointer value, and not just one arbitrary pointer value. The value category too is a specific property of an expression and its result is defined as follows

The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type (8.3.2), an xvalue if T is an rvalue reference to object type, and a prvalue otherwise.

Hence we know that in int t = e;, the result of the conversion sequence is a prvalue, because int is a non-reference type. So if we provide a glvalue, we are in obvious need of a conversion. 3.10p2 further clarifies that to leave no doubt

Whenever a glvalue appears in a context where a prvalue is expected, the glvalue is converted to a prvalue; see 4.1, 4.2, and 4.3.


this is not undefined behaviour.You just don't know its specific values, because there is no initialization. If the variable is global and built-in type so the compiler will put it initialized to the right value. If the variable is local so the compiler not initialize it,So all of the variables are initialized to yourself, don't rely on the compiler.


The behavior is not undefined. The variable is uninitialized and stays with whatever random value uninitialized values start up with. One example from clan'g test suit:

int test7b(int y) {
  int x = x; // expected-note{{variable 'x' is declared here}}
  if (y)
    x = 1;
  // Warn with "may be uninitialized" here (not "is sometimes uninitialized"),
  // since the self-initialization is intended to suppress a -Wuninitialized
  // warning.
  return x; // expected-warning{{variable 'x' may be uninitialized when used here}}
}

Which you can find in clang/test/Sema/uninit-variables.c tests for this case explicitly.

ReferenceURL : https://stackoverflow.com/questions/14935722/does-initialization-entail-lvalue-to-rvalue-conversion-is-int-x-x-ub

반응형