질문자 :James McNellis
C++03에서 표현식은 rvalue 또는 lvalue 입니다.
C++11에서 표현식은 다음과 같을 수 있습니다.
- rvalue
- l값
- x값
- 글 밸류
- 가치
2개의 카테고리가 5개의 카테고리가 되었습니다.
- 이 새로운 범주의 표현은 무엇입니까?
- 이러한 새로운 범주는 기존 rvalue 및 lvalue 범주와 어떤 관련이 있습니까?
- C++0x의 rvalue 및 lvalue 범주는 C++03과 동일합니까?
- 이러한 새로운 범주가 필요한 이유는 무엇입니까? WG21 신들은 우리를 단순한 인간으로 혼란스럽게 하려고 합니까?
나는 이 문서가 그렇게 짧은 소개가 되지 않을 것이라고 생각한다 : n3055
전체 학살은 이동 의미론으로 시작되었습니다. 움직일 수 있고 복사할 수 없는 표현이 생기면 갑자기 이해하기 쉬운 규칙에 따라 움직일 수 있는 표현과 그 방향의 구분이 필요했습니다.
초안을 바탕으로 추측한 바에 따르면 r/l 값 구분은 동일하게 유지되며 움직이는 상황에서만 지저분해집니다.
그것들이 필요합니까? 아마도 우리가 새로운 기능을 상실하고 싶다면 그렇지 않을 것입니다. 그러나 더 나은 최적화를 위해서는 이를 수용해야 합니다.
n3055 인용 :
- lvalue (역사적으로 lvalue는 대입 표현식의 왼쪽에 나타날 수 있기 때문에 소위)는 함수 또는 개체를 지정합니다. [실시 예는 다음의 경우
E
다음, 포인터 타입의 표현이다 *E
로 개체 또는 기능을 언급 좌변 표현식 E
점. 또 다른 예로, 반환형이 lvalue 참조인 함수를 호출한 결과는 lvalue입니다.] - xvalue ("eXpiring" 값)는 또한 일반적으로 수명이 거의 다한 개체를 나타냅니다(예: 리소스를 이동할 수 있음). xvalue는 rvalue 참조와 관련된 특정 종류의 표현식의 결과입니다. [예: 리턴타입이 rvalue 참조인 함수를 호출한 결과는 xvalue이다.]
- glvalue ("일반화된" lvalue)는 lvalue 또는 xvalue 입니다.
- rvalue (역사적으로는 rvalue가 할당 표현식의 오른쪽에 나타날 수 있기 때문에 소위)는 xvalue, 임시 개체 또는 해당 하위 개체 또는 개체와 연결되지 않은 값입니다.
- prvalue ("순수" rvalue)는 xvalue가 아닌 rvalue입니다. [예시: 반환형이 참조가 아닌 함수를 호출한 결과는 prvalue이다]
문제의 문서는 새로운 명명법의 도입으로 인해 발생한 표준의 정확한 변경 사항을 보여주기 때문에 이 질문에 대한 훌륭한 참고 자료입니다.
Kornel Kisielewicz이 새로운 범주의 표현은 무엇입니까?
FCD(n3092) 에는 다음과 같은 훌륭한 설명이 있습니다.
— lvalue(역사적으로는 lvalue가 할당 표현식의 왼쪽에 나타날 수 있기 때문에 그렇게 불림)는 함수 또는 객체를 지정합니다. [ 예: E가 포인터 유형의 표현식인 경우 *E는 E가 가리키는 객체 또는 함수를 참조하는 lvalue 표현식입니다. 다른 예로, 반환 유형이 lvalue 참조인 함수를 호출한 결과는 lvalue입니다. —끝 예 ]
— xvalue("eXpiring" 값)는 또한 일반적으로 수명이 거의 다한 개체를 나타냅니다(예: 리소스를 이동할 수 있음). xvalue는 rvalue 참조를 포함하는 특정 종류의 표현식의 결과입니다(8.3.2). [ 예: 반환 유형이 rvalue 참조인 함수를 호출한 결과는 xvalue입니다. —끝 예 ]
— glvalue("일반화된" lvalue)는 lvalue 또는 xvalue입니다.
— rvalue(역사적으로는 rvalue가 할당 표현식의 오른쪽에 나타날 수 있기 때문에 호출됨)는 xvalue, 임시 객체(12.2) 또는 그 하위 객체, 또는 객체와 연결되지 않은 값입니다.
— prvalue("순수" rvalue)는 xvalue가 아닌 rvalue입니다. [ 예: 반환 유형이 참조가 아닌 함수를 호출한 결과는 prvalue입니다. 12, 7.3e5 또는 true와 같은 리터럴 값도 prvalue입니다. —끝 예 ]
모든 표현식은 이 분류법의 기본 분류 중 하나인 lvalue, xvalue 또는 prvalue에 정확히 속합니다. 표현식의 이 속성을 값 범주라고 합니다. [ 참고: 5절의 각 내장 연산자에 대한 설명은 그것이 산출하는 값의 범주와 예상되는 피연산자의 값 범주를 나타냅니다. 예를 들어, 기본 제공 할당 연산자는 왼쪽 피연산자가 lvalue이고 오른쪽 피연산자가 prvalue이고 결과로 lvalue를 생성할 것으로 예상합니다. 사용자 정의 연산자는 함수이며 기대하고 산출하는 값의 범주는 매개변수와 반환 유형에 따라 결정됩니다. - 끝 메모
하지만 3.10 Lvalue와 rvalue 섹션 전체를 읽는 것이 좋습니다.
이러한 새로운 범주는 기존 rvalue 및 lvalue 범주와 어떤 관련이 있습니까?
다시:
C++0x의 rvalue 및 lvalue 범주는 C++03과 동일합니까?
rvalue의 의미 체계는 특히 이동 의미 체계의 도입과 함께 발전했습니다.
이러한 새로운 범주가 필요한 이유는 무엇입니까?
그래서 이동 구성/할당이 정의되고 지원될 수 있습니다.
Community Wiki마지막 질문으로 시작하겠습니다.
이러한 새로운 범주가 필요한 이유는 무엇입니까?
C++ 표준에는 표현식의 값 범주를 다루는 많은 규칙이 포함되어 있습니다. 일부 규칙은 lvalue와 rvalue를 구분합니다. 예를 들어 과부하 해결과 관련하여. 다른 규칙은 glvalue와 prvalue를 구분합니다. 예를 들어, 불완전하거나 추상적인 유형의 glvalue를 가질 수 있지만 불완전하거나 추상적인 유형의 prvalue는 없습니다. 우리가 이 용어를 사용하기 전에는 lvalue/rvalue를 참조하는 glvalue/prvalue를 실제로 구별해야 하는 규칙이 있었고 의도하지 않게 잘못되었거나 규칙에 대한 설명과 예외가 많이 포함되어 있었습니다. rvalue 참조...". 따라서 glvalues와 prvalues의 개념에 고유한 이름을 지정하는 것이 좋습니다.
이 새로운 범주의 표현은 무엇입니까? 이러한 새로운 범주는 기존 rvalue 및 lvalue 범주와 어떤 관련이 있습니까?
우리는 여전히 C++98과 호환되는 lvalue 및 rvalue라는 용어를 가지고 있습니다. 우리는 방금 rvalue를 xvalue와 prvalue의 두 하위 그룹으로 나누었고 lvalue와 xvalue를 glvalue라고 합니다. Xvalue는 명명되지 않은 rvalue 참조를 위한 새로운 종류의 값 범주입니다. 모든 표현식은 lvalue, xvalue, prvalue의 세 가지 중 하나입니다. 벤 다이어그램은 다음과 같습니다.
______ ______ / X \ / / \ \ | l | x | pr | \ \ / / \______X______/ gl r
함수가 있는 예:
int prvalue(); int& lvalue(); int&& xvalue();
그러나 명명된 rvalue 참조는 lvalue임을 잊지 마십시오.
void foo(int&& t) { // t is initialized with an rvalue expression // but is actually an lvalue expression itself }
sellibitze이러한 새로운 범주가 필요한 이유는 무엇입니까? WG21 신들은 우리를 단순한 인간으로 혼란스럽게 하려고 합니까?
나는 다른 답변(많은 답변이 좋지만)이 이 특정 질문에 대한 답변을 실제로 포착한다고 생각하지 않습니다. 예, 이러한 범주 등이 이동 의미론을 허용하기 위해 존재하지만 한 가지 이유로 복잡성이 존재합니다. 이것은 C++11에서 물건을 옮기는 한 가지 위반 규칙입니다.
의심할 여지 없이 안전한 경우에만 이동해야 합니다.
이것이 이러한 범주가 존재하는 이유입니다. 즉, 안전한 곳에서 가치에 대해 이야기할 수 있고 그렇지 않은 경우 가치에 대해 이야기할 수 있습니다.
초기 버전의 r-값 참조에서는 이동이 쉽게 발생했습니다. 너무 쉽게. 사용자가 실제로 의도하지 않은 경우 암시적으로 물건을 이동할 수 있는 가능성이 충분히 있었습니다.
다음은 물건을 옮기는 것이 안전한 상황입니다.
- 임시 또는 그 하위 객체인 경우. (사전 가치)
- 사용자가 명시적으로 이동을 지시한 경우 .
이렇게 하면:
SomeType &&Func() { ... } SomeType &&val = Func(); SomeType otherVal{val};
이것은 무엇을합니까? 이전 버전의 사양에서는 5개의 값이 입력되기 전에 이동을 유발할 수 있습니다. 물론 그렇습니다. 생성자에 rvalue 참조를 전달했으므로 rvalue 참조를 사용하는 생성자에 바인딩됩니다. 그건 분명해.
여기에는 한 가지 문제가 있습니다. 옮기라고 하지 않으 셨습니다. 오, 당신은 &&
가 단서가 되어야 한다고 말할 수도 있지만 그것이 규칙을 어겼다는 사실을 바꾸지는 않습니다. 임시에는 이름이 없기 때문에 val
임시 수명을 연장했을 수 있지만 이는 임시 가 아님을 의미합니다. 다른 스택 변수와 같습니다.
일시적인 것이 아니고 이사를 요청하지 않았다면 이사가 잘못된 것입니다.
명백한 해결책은 val
을 lvalue로 만드는 것입니다. 이것은 당신이 그것에서 이동할 수 없다는 것을 의미합니다. 알았어 괜찮아; 이름이 지정되었으므로 lvalue입니다.
그렇게 하면 더 이상 SomeType&&
어디에서나 같은 것을 의미한다고 말할 수 없습니다. 이제 명명된 rvalue 참조와 명명되지 않은 rvalue 참조를 구분했습니다. 음, 명명된 rvalue 참조는 lvalue입니다. 그것이 위의 우리의 솔루션이었습니다. 그렇다면 이름 없는 rvalue 참조( Func
에서 반환된 값)를 무엇이라고 부를까요?
lvalue에서 이동할 수 없기 때문에 lvalue가 아닙니다. 그리고 우리는 반환하여 이동 할 수 있어야 &&
; 다른 방법으로 무언가를 움직이라고 명시적으로 말할 수 있습니까? 그것이 결국 std::move
반환하는 것입니다. rvalue(구식)가 아닙니다. 방정식의 왼쪽에 있을 수 있기 때문입니다(실제로는 좀 더 복잡합니다. 이 질문 과 아래 설명 참조). lvalue도 rvalue도 아닙니다. 그것은 새로운 종류의 것입니다.
우리가 가지고 있는 것은 암시적으로 이동할 수 있다는 점을 제외하고 는 lvalue로 취급할 수 있는 값입니다. 우리는 그것을 x값이라고 부릅니다.
xvalue는 다른 두 가지 범주의 값을 얻도록 합니다.
prvalue는 실제로 이전 유형의 rvalue에 대한 새 이름입니다. 즉, xvalue가 아닌 rvalue입니다.
Glvalue는 공통 속성을 많이 공유하기 때문에 한 그룹에 있는 xvalue와 lvalue의 합집합입니다.
따라서 실제로 모든 것은 x값과 이동을 정확하고 특정 위치로만 제한해야 할 필요성으로 귀결됩니다. 이러한 장소는 rvalue 범주에 의해 정의됩니다. prvalue는 암시적 이동이고 xvalue는 명시적 이동입니다( std::move
는 xvalue를 반환함).
Nicol BolasIMHO, 그 의미에 대한 가장 좋은 설명은 Stroustrup + Dániel Sándor 및 Mohan의 예를 고려했습니다.
스트루스트럽:
이제 나는 심각하게 고민했다. 분명히 우리는 교착 상태나 혼란 또는 둘 다로 향하고 있었습니다. 나는 어떤 속성(값)이 독립적인지 알아보기 위해 분석을 하는 데 점심 시간을 보냈습니다. 독립된 속성은 두 개뿐이었습니다.
-
has identity
있고 주소, 포인터, 사용자는 두 복사본이 동일한지 여부 등을 결정할 수 있습니다. -
can be moved from
즉, 불확실하지만 유효한 상태에서 "복사본"의 소스로 떠날 수 있습니다.
이것은 정확히 세 가지 종류의 값이 있다는 결론에 이르렀습니다(대문자를 사용하여 음수를 나타내는 정규식 표기법 사용 – 저는 급했습니다).
이러한 세 가지 기본 가치 분류 외에도 두 가지 독립적인 속성에 해당하는 두 가지 명백한 일반화가 있습니다.
이 도표를 게시판에 올리게 되었습니다.
네이밍
나는 우리가 명명할 수 있는 자유가 제한적이라는 것을 관찰했습니다. 왼쪽에 있는 두 점( iM
및 i
표시됨)은 다소 형식적인 사람들이 lvalues
라고 부르는 것이고 오른쪽에 있는 두 점( m
및 Im
표시됨)은 사람들이 다소 형식적으로 rvalues
호출했습니다. 이것은 우리의 명명에 반영되어야 합니다. 즉, 왼쪽 "다리" W
관련된 이름이 있어야 lvalue
과 오른쪽의 "다리" W
관련 이름이 있어야 rvalue.
나는 이 전체 토론/문제가 rvalue 참조 및 이동 의미론의 도입에서 발생한다는 점에 주목합니다. rvalues
와 lvalues
로 구성된 Strachey의 세계에는 존재하지 않습니다. 어떤 사람은 다음과 같은 아이디어를 관찰했습니다.
- 모든
value
은 lvalue
또는 rvalue
-
lvalue
하지 rvalue
하고 rvalue
아니 어서 lvalue
매우 유용한 속성은 우리의 의식에 깊숙이 박혀 있으며 이 이분법의 흔적은 초안 표준 전체에서 찾을 수 있습니다. 우리 모두는 이러한 속성을 보존해야 한다는 데 동의했습니다. 이것은 우리의 명명 선택을 더욱 제한했습니다. rvalue
m
(일반화)을 의미하는 것을 관찰했습니다. 따라서 표준 라이브러리의 예상과 텍스트를 보존하기 위해 W
rvalue.
로 명명되어야 합니다.
이것은 명명에 대한 집중적인 논의로 이어졌습니다. lvalue.
를 결정해야 했습니다. lvalue
는 iM
또는 일반화 i
의미해야 합니까? Doug Gregor가 이끄는 우리는 핵심 언어 표현에서 lvalue
라는 단어가 둘 중 하나를 의미할 수 있는 장소를 나열했습니다. 목록이 만들어졌고 대부분의 경우 가장 까다롭고 깨지기 쉬운 텍스트에서 lvalue
현재 iM
의미합니다. 이것은 "예전에는" 아무것도 움직이지 않았기 때문에 lvalue의 고전적인 의미입니다. move
C++0x
의 새로운 개념입니다. W
lvalue
의 왼쪽 위 지점에 이름을 지정하면 lvalue
또는 rvalue
아닌 둘 다라는 속성을 얻을 수 있습니다.
W
의 왼쪽 위 지점은 lvalue
이고 오른쪽 아래 지점은 rvalue.
왼쪽 하단과 오른쪽 상단이 점을 만드는 것은 무엇입니까? 왼쪽 하단 포인트는 이동을 허용하는 고전적인 lvalue의 일반화입니다. 따라서 generalized lvalue.
우리는 그것을 glvalue.
당신은 약어에 대해 불평할 수 있지만 (내 생각에) 논리로는 그렇지 않습니다. 우리는 진지하게 사용할 때 generalized lvalue
가 어쨌든 어떻게든 축약될 것이라고 가정했으므로 즉시 수행하는 것이 좋습니다(또는 혼란의 위험이 있음). W 의 오른쪽 상단 지점은 오른쪽 하단보다 덜 일반적입니다(지금도 마찬가지로 rvalue
라고 함). 그 점은 (소멸자를 제외하고) 다시 참조할 수 없기 때문에 이동할 수 있는 객체의 원래 순수한 개념을 나타냅니다. generalized lvalue
와 대조적으로 specialized rvalue
라는 문구를 좋아했지만 prvalue
축약된 pure rvalue
이겼습니다. 따라서 W의 왼쪽 다리는 lvalue
와 glvalue
이고 오른쪽 다리는 prvalue
와 rvalue.
덧붙여서, 모든 값은 glvalue 또는 prvalue이지만 둘 다인 것은 아닙니다.
이렇게 하면 W
: im
; 즉, 동일성이 있고 이동할 수 있는 값입니다. 우리는 그 난해한 야수들에게 좋은 이름으로 우리를 안내하는 어떤 것도 정말로 가지고 있지 않습니다. 그것들은 (초안) 표준 텍스트로 작업하는 사람들에게 중요하지만 가명이 될 가능성은 낮습니다. 우리는 우리를 안내하는 명명에 대한 실제 제약을 찾지 못했기 때문에 중앙에 'x', 알려지지 않은, 이상한, xpert 전용 또는 x-등급을 선택했습니다.
Ivan Kush소개
ISOC++11(공식적으로 ISO/IEC 14882:2011)은 C++ 프로그래밍 언어 표준의 가장 최신 버전입니다. 여기에는 몇 가지 새로운 기능과 개념이 포함되어 있습니다. 예를 들면 다음과 같습니다.
- rvalue 참조
- xvalue, glvalue, prvalue 표현식 값 범주
- 이동 의미론
새로운 표현식 값 범주의 개념을 이해하려면 rvalue 및 lvalue 참조가 있음을 알아야 합니다. rvalue가 비 const rvalue 참조로 전달될 수 있다는 것을 아는 것이 좋습니다.
int& r_i=7; // compile error int&& rr_i=7; // OK
작업 초안 N3337(공개된 ISOC++11 표준과 가장 유사한 초안)에서 Lvalue 및 rvalue라는 하위 섹션을 인용하면 가치 범주의 개념에 대한 직관을 얻을 수 있습니다.
3.10 Lvalue와 rvalue [basic.lval]
1 표현식은 그림 1의 분류에 따라 분류됩니다.
- lvalue(역사적으로는 lvalue가 할당 표현식의 왼쪽에 나타날 수 있기 때문에 그렇게 불림)는 함수 또는 객체를 지정합니다. [ 예: E가 포인터 유형의 표현식인 경우 *E는 E가 가리키는 객체 또는 함수를 참조하는 lvalue 표현식입니다. 다른 예로, 반환 유형이 lvalue 참조인 함수를 호출한 결과는 lvalue입니다. —끝 예 ]
- xvalue("eXpiring" 값)는 일반적으로 수명이 거의 다한 개체를 나타냅니다(예: 리소스를 이동할 수 있음). xvalue는 rvalue 참조를 포함하는 특정 종류의 표현식의 결과입니다(8.3.2). [ 예: 반환 유형이 rvalue 참조인 함수를 호출한 결과는 xvalue입니다. —끝 예 ]
- glvalue("일반화된" lvalue)는 lvalue 또는 xvalue입니다.
- rvalue(rvalue가 할당 표현식의 오른쪽에 나타날 수 있기 때문에 역사적으로 그렇게 불림)는 xvalue입니다.
임시 객체(12.2) 또는 그 하위 객체, 또는 다음이 아닌 값
개체와 연결됩니다. - prvalue("순수" rvalue)는 xvalue가 아닌 rvalue입니다. [ 예: 반환형이 a가 아닌 함수를 호출한 결과
참조는 prvalue입니다. 12, 7.3e5 또는
true도 prvalue입니다. —끝 예 ]
모든 표현식은 이 분류법의 기본 분류 중 하나인 lvalue, xvalue 또는 prvalue에 정확히 속합니다. 표현식의 이 속성을 값 범주라고 합니다.
그러나 이 하위 섹션이 개념을 명확하게 이해하기에 충분하다고 확신할 수 없습니다. "보통"이 실제로 일반적이지 않고 "거의 수명이 다할 때"가 실제로 구체적이지 않고 "rvalue 참조 포함"이 명확하지 않기 때문입니다. 및 "예: 반환 유형이 rvalue 참조인 함수를 호출한 결과는 x값입니다." 뱀이 꼬리를 물고 있는 것 같다.
주요 가치 범주
모든 표현식은 정확히 하나의 기본 값 범주에 속합니다. 이러한 값 범주는 lvalue, xvalue 및 prvalue 범주입니다.
lvalue
E가 E 외부에서 액세스할 수 있도록 하는 ID(주소, 이름 또는 별칭)가 이미 있는 엔터티를 참조하는 경우에만 식 E는 lvalue 범주에 속합니다.
#include <iostream> int i=7; const int& f(){ return i; } int main() { std::cout<<&"www"<<std::endl; // The expression "www" in this row is an lvalue expression, because string literals are arrays and every array has an address. i; // The expression i in this row is an lvalue expression, because it refers to the same entity ... i; // ... as the entity the expression i in this row refers to. int* p_i=new int(7); *p_i; // The expression *p_i in this row is an lvalue expression, because it refers to the same entity ... *p_i; // ... as the entity the expression *p_i in this row refers to. const int& r_I=7; r_I; // The expression r_I in this row is an lvalue expression, because it refers to the same entity ... r_I; // ... as the entity the expression r_I in this row refers to. f(); // The expression f() in this row is an lvalue expression, because it refers to the same entity ... i; // ... as the entity the expression f() in this row refers to. return 0; }
x값
식 E는 다음과 같은 경우에만 xvalue 범주에 속합니다.
— 암시적이든 명시적이든 반환 유형이 반환되는 객체 유형에 대한 rvalue 참조인 함수를 호출한 결과, 또는
int&& f(){ return 3; } int main() { f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type. return 0; }
— 객체 유형에 대한 rvalue 참조로의 캐스트, 또는
int main() { static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type. std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7). return 0; }
— 객체 표현식이 x값인 비참조 유형의 비정적 데이터 멤버를 지정하는 클래스 멤버 액세스 표현식, 또는
struct As { int i; }; As&& f(){ return As(); } int main() { f().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category. return 0; }
— 첫 번째 피연산자가 xvalue이고 두 번째 피연산자가 데이터 멤버에 대한 포인터인 멤버에 대한 포인터 식.
위 규칙의 효과는 객체에 대한 명명된 rvalue 참조가 lvalue로 처리되고 객체에 대한 명명되지 않은 rvalue 참조가 xvalue로 처리된다는 점에 유의하십시오. 함수에 대한 rvalue 참조는 명명 여부에 관계없이 lvalue로 처리됩니다.
#include <functional> struct As { int i; }; As&& f(){ return As(); } int main() { f(); // The expression f() belongs to the xvalue category, because it refers to an unnamed rvalue reference to object. As&& rr_a=As(); rr_a; // The expression rr_a belongs to the lvalue category, because it refers to a named rvalue reference to object. std::ref(f); // The expression std::ref(f) belongs to the lvalue category, because it refers to an rvalue reference to function. return 0; }
가치
표현식 E는 E가 lvalue도 xvalue 범주에도 속하지 않는 경우에만 prvalue 범주에 속합니다.
struct As { void f(){ this; // The expression this is a prvalue expression. Note, that the expression this is not a variable. } }; As f(){ return As(); } int main() { f(); // The expression f() belongs to the prvalue category, because it belongs neither to the lvalue nor to the xvalue category. return 0; }
혼합 가치 카테고리
두 가지 중요한 혼합 가치 범주가 더 있습니다. 이러한 값 범주는 rvalue 및 glvalue 범주입니다.
rvalue
표현식 E는 E가 xvalue 범주 또는 prvalue 범주에 속하는 경우에만 rvalue 범주에 속합니다.
이 정의는 E가 E YET 외부에서 액세스할 수 있는 ID가 없는 엔티티를 참조하는 경우에만 표현식 E가 rvalue 범주에 속한다는 것을 의미합니다.
글 값
E가 lvalue 범주 또는 xvalue 범주에 속하는 경우에만 표현식 E는 glvalue 범주에 속합니다.
실용적인 규칙
Scott Meyer는 rvalue와 lvalue를 구별하는 매우 유용한 경험 법칙을발표했습니다.
- 표현식의 주소를 사용할 수 있는 경우 표현식은 lvalue입니다.
- 식의 유형이 lvalue 참조(예: T& 또는 const T& 등)인 경우 해당 식은 lvalue입니다.
- 그렇지 않으면 표현식은 rvalue입니다. 개념적으로(그리고 일반적으로 실제로도) rvalue는 함수에서 반환되거나 암시적 유형 변환을 통해 생성된 것과 같은 임시 객체에 해당합니다. 대부분의 리터럴 값(예: 10 및 5.3)도 rvalue입니다.
Dániel Sándor나는 가치 카테고리에 대한 cppreference.com 설명을 볼 때까지 오랫동안 이것으로 어려움을 겪었습니다.
실제로는 다소 간단한데 외우기 어려운 방식으로 설명되는 경우가 많다는 것을 알게 되었습니다. 여기에서는 매우 개략적으로 설명합니다. 페이지의 일부를 인용하겠습니다.
기본 카테고리
기본 값 범주는 표현식의 두 가지 속성에 해당합니다.
다음과 같은 표현:
- ID가 있고 이동할 수 없는 것을 lvalue 식이 라고 합니다.
- ID가 있고 이동할 수 있는 것을 xvalue 표현식 이라고 합니다.
- ID가 없고 에서 이동할 수 있는 것을 prvalue 식이 라고 합니다.
- ID가 없으며 이동할 수 없습니다. 사용되지 않습니다.
l값
lvalue("왼쪽 값") 표현식은 ID가 있고 에서 이동할 수 없는 표현식입니다.
rvalue(C++11까지), prvalue(C++11부터)
prvalue("순수 rvalue") 표현식은 ID가 없고 에서 이동할 수 있는 표현식입니다.
x값
xvalue("만료 값") 표현식은 ID 가 있고 에서 이동할 수 있는 표현식입니다.
글 밸류
glvalue("일반화된 lvalue") 표현식은 lvalue 또는 xvalue인 표현식입니다. 그것은 정체성을 가지고 있습니다. 에서 옮겨질 수도 있고 그렇지 않을 수도 있습니다.
rvalue(C++11부터)
rvalue("올바른 값") 표현식은 prvalue 또는 xvalue인 표현식입니다. 에서 이동할 수 있습니다 . 아이덴티티가 있을 수도 있고 없을 수도 있습니다.
그래서 그것을 테이블에 넣어 보자:
| (= rvalue)에서 이동할 수 있습니다. | 다음에서 이동할 수 없습니다. |
---|
ID가 있음(= glvalue) | x값 | l값 |
신원 없음 | 가치 | 사용하지 않음 |
Felix DombekC++03의 범주는 표현식 속성에 rvalue 참조를 올바르게 도입하는 것을 캡처하기에는 너무 제한적입니다.
그것들의 도입으로, 무명 rvalue 참조는 rvalue로 평가되어 과부하 해결은 rvalue 참조 바인딩을 선호하여 복사 생성자보다 이동 생성자를 선택하게 된다고 합니다. 그러나 이것은 예를 들어 동적 유형 및 자격과 관련된 모든 문제를 일으키는 것으로 나타났습니다.
이를 보여주기 위해 다음을 고려하십시오.
int const&& f(); int main() { int &&i = f(); // disgusting! }
xvalue 이전 초안에서는 이것이 허용되었습니다. C++03에서 클래스가 아닌 유형의 rvalue는 결코 cv로 한정되지 않기 때문입니다. const
가 rvalue-reference의 경우에 적용되도록 의도 되었습니다. 여기서 우리는 객체(= 메모리!)를 참조하고 클래스가 아닌 rvalue에서 const를 삭제하는 것은 주로 주변에 객체가 없기 때문입니다.
동적 유형에 대한 문제는 유사한 성격을 가집니다. C++03에서 클래스 유형의 rvalue에는 알려진 동적 유형이 있습니다. 이는 해당 표현식의 정적 유형입니다. 다른 방식으로 사용하려면 lvalue로 평가되는 참조 또는 역참조가 필요합니다. 명명되지 않은 rvalue 참조에서는 그렇지 않지만 다형성 동작을 나타낼 수 있습니다. 그래서 그것을 해결하기 위해,
명명되지 않은 rvalue 참조는 xvalue 가 됩니다. 그것들은 한정될 수 있고 잠재적으로 다른 동적 유형을 가질 수 있습니다. 의도한 대로 오버로딩하는 동안 rvalue 참조를 선호하며 const가 아닌 lvalue 참조에 바인딩하지 않습니다.
이전에는 rvalue(리터럴, 비참조 유형으로 캐스트하여 생성된 객체) 였던 것이 이제 prvalue 가 됩니다. 오버로딩 중 xvalue와 동일한 기본 설정을 갖습니다.
이전에 lvalue였던 것이 lvalue로 유지됩니다.
그리고 두 개의 그룹화를 수행하여 한정될 수 있고 다른 동적 유형을 가질 수 있는 항목( glvalues )과 오버로드가 rvalue 참조 바인딩을 선호하는 항목( rvalues )을 캡처합니다.
Johannes Schaub - litb이전 답변에서 가치 범주에 대한 이론을 철저히 다루었으므로 추가하고 싶은 또 다른 사항이 있습니다. 실제로 가지고 놀고 테스트할 수 있습니다.
값 범주에 대한 몇 가지 실습 실험을 위해 decltype 지정자를 사용할 수 있습니다. 그 동작은 세 가지 기본 값 범주(xvalue, lvalue 및 prvalue)를 명시적으로 구분합니다.
전처리기를 사용하면 타이핑을 줄일 수 있습니다 ...
기본 카테고리:
#define IS_XVALUE(X) std::is_rvalue_reference<decltype((X))>::value #define IS_LVALUE(X) std::is_lvalue_reference<decltype((X))>::value #define IS_PRVALUE(X) !std::is_reference<decltype((X))>::value
혼합 카테고리:
#define IS_GLVALUE(X) (IS_LVALUE(X) || IS_XVALUE(X)) #define IS_RVALUE(X) (IS_PRVALUE(X) || IS_XVALUE(X))
이제 cppreference on value category 의 모든 예를 (거의) 재현할 수 있습니다.
다음은 C++17의 몇 가지 예입니다(간단한 static_assert의 경우):
void doesNothing(){} struct S { int x{0}; }; int x = 1; int y = 2; S s; static_assert(IS_LVALUE(x)); static_assert(IS_LVALUE(x+=y)); static_assert(IS_LVALUE("Hello world!")); static_assert(IS_LVALUE(++x)); static_assert(IS_PRVALUE(1)); static_assert(IS_PRVALUE(x++)); static_assert(IS_PRVALUE(static_cast<double>(x))); static_assert(IS_PRVALUE(std::string{})); static_assert(IS_PRVALUE(throw std::exception())); static_assert(IS_PRVALUE(doesNothing())); static_assert(IS_XVALUE(std::move(s))); // The next one doesn't work in gcc 8.2 but in gcc 9.1. Clang 7.0.0 and msvc 19.16 are doing fine. static_assert(IS_XVALUE(S().x));
혼합된 범주는 일단 기본 범주를 파악하면 지루합니다.
더 많은 예제(및 실험)를 보려면 컴파일러 탐색기 에서 다음 링크를 확인하세요. 그러나 어셈블리를 읽는 것을 귀찮게하지 마십시오. 모든 일반 컴파일러에서 작동하는지 확인하기 위해 많은 컴파일러를 추가했습니다.
thebrandre이러한 새로운 범주는 기존 rvalue 및 lvalue 범주와 어떤 관련이 있습니까?
C++03 lvalue는 여전히 C++11 lvalue인 반면 C++03 rvalue는 C++11에서 prvalue라고 합니다.
fredoverflowStroustrup을 읽고 rvalue/lvalue 구별을 이해했다고 생각한 후에도 나를 혼란스럽게 한 점에 대한 위의 훌륭한 답변에 대한 한 가지 부록입니다. 당신이 볼 때
int&& a = 3
,
매우 읽기 끌리기 int&&
형식으로하고 결론 a
를 rvalue입니다. 그것은 아닙니다:
int&& a = 3; int&& c = a; //error: cannot bind 'int' lvalue to 'int&&' int& b = a; //compiles
a
는 이름을 가지고 있으며 사실상 lvalue입니다. &&
a
유형의 일부로 생각하지 마십시오. 바인딩할 수 a
항목을 알려주는 것입니다.
T&&
유형 인수에 특히 중요합니다. 당신이 쓰는 경우
Foo::Foo(T&& _t) : t{_t} {}
_t
를 t
복사합니다. 당신은 필요
Foo::Foo(T&& _t) : t{std::move(_t)} {}
이동하려는 경우. move
생략했을 때 내 컴파일러가 나에게 경고했을까요!
Mohan이것은 C++ 위원회가 C++11에서 이동 의미론을 정의하는 데 사용한 용어입니다. 여기 이야기가 있습니다.
정확한 정의, 긴 규칙 목록 또는 다음과 같은 인기 있는 다이어그램을 고려할 때 용어를 이해하기가 어렵습니다.
일반적인 예가 있는 벤 다이어그램에서는 더 쉽습니다.
원래:
- 모든 표현식은 lvalue 또는 rvalue입니다.
- lvalue는 ID가 있으므로 나중에 사용할 수 있으므로 복사해야 합니다.
- 임시(prvalue) 또는 명시적으로 이동(xvalue)하기 때문에 rvalue를 이동할 수 있습니다.
이제 좋은 질문은 두 개의 직교 속성("식별 있음" 및 "이동 가능")이 있는 경우 lvalue, xvalue 및 prvalue를 완성하는 네 번째 범주는 무엇입니까? 이는 ID가 없고(따라서 나중에 액세스할 수 없음) 이동할 수 없는(해당 값을 복사해야 함) 표현식입니다. 이것은 단순히 유용하지 않으므로 이름이 지정되지 않았습니다.
0xF이것은 내가 쓰고 있는 고도로 시각적인 C++ 책을 위해 만든 벤 다이어그램입니다. 이 책은 곧 개발 중에 leanpub에 게시할 예정입니다.
다른 답변은 단어로 더 자세히 설명하고 유사한 다이어그램을 보여줍니다. 그러나 정보의 이 프레젠테이션이 참조에 추가로 상당히 완전하고 유용하기를 바랍니다.
이 주제에 대한 주요 내용은 표현식에 identity 및 moveability 의 두 가지 속성이 있다는 것입니다. 첫 번째는 어떤 것이 존재하는 "견고함"을 다룬다. 이는 C++ 추상 기계가 최적화를 통해 코드를 적극적으로 변경하고 축소하도록 허용되고 권장되기 때문에 중요합니다. 즉, ID가 없는 것은 짓밟히기 전에 컴파일러의 마음이나 레지스터에 잠시 동안만 존재할 수 있음을 의미합니다. 에. 그러나 그러한 데이터 조각도 내부에 있는 것을 재활용할 경우 문제를 일으키지 않는 것이 보장됩니다. 사용할 방법이 없기 때문입니다. 따라서 임시에 대한 참조를 캡처하여 lvalue로 업그레이드하고 수명을 연장할 수 있도록 이동 의미 체계가 발명되었습니다.
이동 의미는 원래 임시를 그냥 버리는 것이 아니라 다른 사람이 사용할 수 있도록 버리는 것이었습니다.
당신이 옥수수 빵을 줄 때, 당신이 그것을 주는 사람이 이제 그것을 소유합니다. 그들은 그것을 소비합니다. 옥수수 빵을 주면 먹거나 소화하려고 해서는 안 됩니다. 어쨌든 그 옥수수 빵은 쓰레기통으로 향하고 있었을지 모르지만 지금은 배로 향하고 있습니다. 더 이상 당신의 것이 아닙니다.
C++ 영역에서 리소스를 "소비"한다는 개념은 리소스가 이제 우리 소유이므로 필요한 정리를 수행하고 개체가 다른 곳에서 액세스되지 않도록 해야 함을 의미합니다. 종종 그것은 새로운 물건을 만들기 위해 배짱을 빌리는 것을 의미합니다. 나는 그것을 "장기 기증"이라고 부릅니다. 일반적으로 우리는 객체에 포함된 포인터 또는 참조 또는 이와 유사한 것에 대해 이야기하고 있으며 이러한 포인터 또는 참조는 소멸되지 않는 프로그램의 다른 곳에서 데이터를 참조하기 때문에 이러한 포인터 또는 참조를 계속 유지하려고 합니다.
따라서 rvalue 참조를 사용하는 함수 오버로드를 작성할 수 있으며 임시(prvalue)가 전달된 경우 호출될 오버로드입니다. 함수에서 가져온 rvalue 참조에 바인딩하면 새 lvalue가 생성되어 임시의 수명을 연장하여 함수 내에서 사용할 수 있습니다.
어느 시점에서 우리는 종종 한 범위에서 완료되었지만 다른 범위에서 잠식하고 싶은 lvalue 비임시 데이터를 가지고 있다는 것을 깨달았습니다. 그러나 그들은 rvalue가 아니므로 rvalue 참조에 바인딩되지 않습니다. 그래서 우리는 std::move
만들었습니다. 이것은 lvalue에서 rvalue 참조로의 멋진 캐스트입니다. 이러한 데이텀은 xvalue입니다. 이전 lvalue는 이제 임시인 것처럼 작동하므로 이동할 수도 있습니다.
Daniel Russell출처 : http:www.stackoverflow.com/questions/3601602/what-are-rvalues-lvalues-xvalues-glvalues-and-prvalues