rvalue, lvalue, xvalue, glvalue 및 prvalue는 무엇입니까?


1356

C ++ 03에서 표현식은 rvalue 또는 lvalue 입니다.

C ++ 11에서 표현식은 다음과 같습니다.

  1. rvalue
  2. lvalue
  3. x 값
  4. glvalue
  5. prvalue

두 가지 범주가 다섯 가지 범주가되었습니다.

  • 이 새로운 표현 범주는 무엇입니까?
  • 이러한 새 범주는 기존 rvalue 및 lvalue 범주와 어떤 관련이 있습니까?
  • C ++ 0x의 rvalue 및 lvalue 범주는 C ++ 03의 범주와 동일합니까?
  • 이 새로운 카테고리가 필요한 이유는 무엇입니까? 있습니까 WG21의 신들은 우리 단순한 인간을 혼란스럽게하려고?

9
@Philip Potter : C ++ 03에서? 예. 표준 lvalue-to-rvalue 변환이 있으므로 lvalue를 rvalue로 사용할 수 있습니다.
James McNellis

14
@Tyler : "할당 할 수 있으면 lvalue이고, 그렇지 않으면 rvalue입니다." -> 잘못, 당신은 클래스 rvalues에 할당 할 수 있습니다 : string("hello") = string("world").
fredoverflow

4
이것이 가치 범주입니다. 식에 더 많은 속성이있을 수 있습니다. 여기에는 비트 필드 (true / false), 임시 (true / false) 및 유형 (유형)이 포함됩니다.
Johannes Schaub-litb

30
나는 위의 Fred의 링크가 여기의 대답보다 낫다고 생각합니다. 그러나 링크는 죽었습니다. 그것은 이동되었습니다 stroustrup.com/terminology.pdf
R. 마르틴 페르난데스에게

74
C ++에서 심지어 종류의 유형이
nielsbot

답변:


634

나는이 문서가 그렇게 짧은 소개가 아니라고 생각한다 : n3055

전체 학살은 이동 의미론으로 시작되었습니다. 일단 이동하고 복사 할 수없는 표현식이 있으면, 규칙을 쉽게 파악하기 위해 이동할 수있는 표현식과 방향을 구별해야했습니다.

내가 초안을 기반으로 추측 한 것으로부터 r / l 값 구분은 동일하게 유지되며 움직이는 것의 맥락에서만 혼란스러워집니다.

그들이 필요합니까? 우리가 새로운 기능을 잃고 싶을 수도 있습니다. 그러나 더 나은 최적화를 위해서는 아마도 그것들을 수용해야합니다.

인용 n3055 :

  • 좌변 기능 또는 목적을 나타낸다 (lvalues이 과제 식의 좌측에 표시 될 수 있으므로 역사적 소위). [예 : E포인터 유형의 표현식 인 경우 *E 가리키는 객체 또는 함수를 참조하는 lvalue 표현식 E 입니다. 다른 예로, 반환 유형이 lvalue 참조 인 함수를 호출 한 결과는 lvalue입니다.]
  • 가 xValue (AN "만료"값)도 (따라서 자원은, 예를 들어, 이동 될 수 있음)는 일반적으로 수명의 끝 부분에서, 객체를 의미한다. xvalue는 rvalue 참조와 관련된 특정 종류의 표현식의 결과입니다. [예 : 반환 유형이 rvalue 참조 인 함수를 호출 한 결과는 xvalue입니다.]
  • glvalue (좌변 "일반화는")는 인 좌변 또는 가 xValue .
  • r- 수치 (우변이 과제 식의 오른쪽에 표시 될 수 있으므로 역사적 소위)는가 xValue 임시 객체 또는 그 하위 객체 또는 객체와 연관되지 않은 값이다.
  • prvalue ( "순수"를 rvalue)는가 xValue 아닌를 rvalue입니다. [예 : 반환 유형이 참조가 아닌 함수를 호출 한 결과는 prvalue입니다]

문제의 문서는 새로운 명명법 도입의 결과로 발생한 표준의 정확한 변화를 보여 주므로이 질문에 대한 훌륭한 참고 자료입니다.


감사합니다.이 답변은 정말 유용합니다! 그러나 내 컴파일러는 xvalue 및 prvalue에 대한 예제와 동의하지 않습니다. 그것들은 정반대입니다. rvalue 참조로 반환하면 prvalue가 제공되고 값으로 반환하면 xvalue가 제공됩니다. 그것들을 섞어 놓았습니까, 아니면 테스트 베드가 깨졌습니까? GCC 4.6.1, svn의 clang 및 MSVC 로이 작업을 시도했으며 모두 동일한 동작을 보여줍니다.
Kim Gräsman

죄송합니다. 링크를 따라 가서 예제가 소스에 있음을 알았습니다. 나는 표준의 나의 사본을 찾아서 그것이 무엇을 말하는지 알아볼 것이다.
Kim Gräsman

4
나는 테스트 다양한 표현 여기에서 매크로를 사용 stackoverflow.com/a/6114546/96963는 그들이 일을 오진하는 것이 될 수있다.
Kim Gräsman

1
xvalue를 추가하는 것은 이동 의미 체계를위한 것이 아닙니다. lvalue와 rvalue만으로도 이동 시맨틱, 완벽한 정방향 및 rvalue 참조가 여전히 잘 작동합니다. 나는 xvalue가 decltype 연산자를위한 것이라고 생각합니다. 피연산자 표현식이 xvalue이면 decltype은 rvalue 참조의 유형을 제공합니다.
리간드

1
@MuhamedCicak "모든 표현은 lvalue 또는 rvalue"입니다. 그리고 표준 (또는 문서 n3055)은 그것이 틀렸다고 말하지 않습니다. 이 문장이 엇갈린 이유는 문서의 두 버전 간 변경 사항을보고 있었기 때문입니다. 더 정확한 설명이 추가 된 후 불필요 해 졌기 때문에 문장이 제거되었습니다.
최대

337

이 새로운 표현 범주는 무엇입니까?

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 Lvalues와 rvalues 전체 섹션을 읽는 것이 좋습니다 .

이러한 새 범주는 기존 rvalue 및 lvalue 범주와 어떤 관련이 있습니까?

다시:

분류

C ++ 0x의 rvalue 및 lvalue 범주는 C ++ 03의 범주와 동일합니까?

rvalue의 시맨틱은 특히 이동 시맨틱의 도입으로 발전했습니다.

이 새로운 카테고리가 필요한 이유는 무엇입니까?

따라서 이동 구성 / 할당을 정의하고 지원할 수 있습니다.


54
나는 여기 다이어그램을 좋아한다. "모든 표현은이 분류 체계의 기본 분류 중 하나 인 lvalue, xvalue 또는 prvalue에 속합니다."로 답을 시작하는 것이 도움이 될 것입니다. 그런 다음 다이어그램을 사용하여 glvalue와 rvalue를 만들기 위해이 세 가지 기본 클래스가 결합 된 것을 쉽게 표시 할 수 있습니다.
Aaron McDaid

2
"is glvalue"는 "is prvalue가 아님"에 해당하고 "is rvalue"는 "lvalue가 아님"에 해당합니다.
Vladimir Reshetnikov 1

2
이것은 나를 가장 도왔다 : bajamircea.github.io/assets/2016-04-07-move-forward/… (값 범주의 벤 다이어그램)
John P

1
@AaronMcDaid 안녕하세요, 빠른 질문 당신은 / 사람이 대답 할 수 있다면 ... 왜 이름을하지 glvaluelvaluelvalue같은 plvalue, 일관되게?
Vijay Chavda

184

마지막 질문부터 시작하겠습니다.

이 새로운 카테고리가 필요한 이유는 무엇입니까?

C ++ 표준에는 표현식의 값 범주를 처리하는 많은 규칙이 포함되어 있습니다. 일부 규칙은 lvalue와 rvalue를 구별합니다. 예를 들어 과부하 해결과 관련하여. 다른 규칙은 glvalue와 prvalue를 구별합니다. 예를 들어 불완전하거나 추상적 인 유형의 glvalue를 가질 수 있지만 불완전하거나 추상적 인 유형의 prvalue는 없습니다. 이 용어를 사용하기 전에 실제로 lvalue / rvalue를 참조하는 glvalue / prvalue를 구별해야하는 규칙은 의도 치 않게 잘못되었거나 규칙에 대한 많은 설명과 예외가 포함되어 있습니다. rvalue 참조 ... ". 따라서 glvalues와 prvalues의 개념에 자체 이름을 부여하는 것이 좋습니다.

이 새로운 표현 범주는 무엇입니까? 이러한 새 범주는 기존 rvalue 및 lvalue 범주와 어떤 관련이 있습니까?

우리는 여전히 C ++ 98과 호환되는 lvalue와 rvalue라는 용어를 가지고 있습니다. rvalue를 xvalues와 prvalues라는 두 개의 하위 그룹으로 나누고 lvalues와 xvalues를 glvalues라고합니다. 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
}

165

이 새로운 카테고리가 필요한 이유는 무엇입니까? WG21 신들은 단지 우리를 단순한 필사자들과 혼동 시키려고 노력하고 있습니까?

나는 다른 답변들 (많은 것이 좋지만)이 실제로이 특정 질문에 대한 답변을 포착한다고 생각하지 않습니다. 그렇습니다. 이러한 범주와 같은 범주는 이동 의미론을 허용하기 위해 존재하지만 한 가지 이유로 복잡성이 존재합니다. 이것은 C ++ 11에서 물건을 움직이는 규칙을 위반하는 것입니다.

당신은 의심 할 여지없이 그렇게하는 것이 안전 할 때에 만 움직여야합니다.

그렇기 때문에 이러한 범주가 존재하는 이유는 안전한 값에 대해 이야기 할 수 있고 그렇지 않은 값에 대해 이야기하는 것입니다.

초기 버전의 r- 값 참조에서는 이동이 쉽게 발생했습니다. 너무 쉽다. 사용자가 실제로 의도하지 않았을 때 암시 적으로 물건을 움직일 가능성이 많을 정도로 쉽게.

무언가를 옮기는 것이 안전한 상황은 다음과 같습니다.

  1. 임시 또는 하위 개체 인 경우 (prvalue)
  2. 사용자가 명시 적으로 이동한다고 말한 경우 .

이렇게하면 :

SomeType &&Func() { ... }

SomeType &&val = Func();
SomeType otherVal{val};

이것은 무엇을 하는가? 이전 버전의 스펙에서는 5 개의 값이 오기 전에 이동을 유발했습니다. 물론 그렇습니다. rvalue 참조를 생성자에 전달 했으므로 rvalue 참조를 취하는 생성자에 바인딩됩니다. 분명합니다.

이것에는 하나의 문제가 있습니다. 당신은 그것을 이동 하도록 요청 하지 않았습니다 . 오, 당신은 &&단서가되어야 한다고 말할 수도 있지만, 그것이 규칙을 어겼다는 사실을 바꾸지는 않습니다. val임시 이름이 없기 때문에 임시가 아닙니다. 임시 수명을 연장했을 수도 있지만 이는 일시적 이지 않음을 의미합니다 . 다른 스택 변수와 같습니다.

일시적이지 않고 이동하도록 요청하지 않은 경우 이동이 잘못되었습니다.

확실한 해결책은 vallvalue 를 만드는 것입니다. 이것은 당신이 그것에서 이동할 수 없다는 것을 의미합니다. 알았어 괜찮아; 이름이 붙어서 lvalue입니다.

그렇게하면 더 이상 SomeType&&같은 것을 의미 한다고 말할 수 없습니다 . 명명 된 rvalue 참조와 명명되지 않은 rvalue 참조를 구분했습니다. 명명 된 rvalue 참조는 lvalue입니다. 그것은 우리의 해결책이었습니다. 명명되지 않은 rvalue 참조 ( Func위 의 반환 값 )를 무엇이라고합니까?

lvalue에서 이동할 수 없으므로 lvalue가 아닙니다. 그리고 우리는 필요 을 반환하여 이동할 수 있도록 &&; 뭔가 다른 것을 명시 적으로 말할 수있는 방법은 무엇입니까? 그것이 결국 std::move돌아 오는 것입니다. 그것은 방정식의 왼쪽에있을 수 있기 때문에 rvalue (구식)가 아닙니다 (사실 실제로는 조금 더 복잡합니다. 이 질문 과 아래의 주석을 참조하십시오). lvalue 또는 rvalue가 아닙니다. 새로운 종류의 것입니다.

우리가 가진 것은 암시 적으로 이동할 수 있다는 것을 제외하고 는 lvalue로 취급 할 수있는 가치입니다 . 우리는 그것을 xvalue라고 부릅니다.

xvalues는 다른 두 가지 범주의 값을 얻습니다.

  • prvalue는 실제로 이전 유형의 rvalue에 대한 새로운 이름입니다. 즉 xvalue 가 아닌 rvalue입니다.

  • Glvalue는 한 그룹에서 xvalue와 lvalue의 합집합으로, 많은 속성을 공통으로 공유하기 때문입니다.

따라서 실제로 모든 것은 xvalues에 따라 달라지며 정확하게 특정 장소로만 움직임을 제한해야합니다. 이러한 장소는 rvalue 범주로 정의됩니다. prvalues는 암시 적 이동이며 xvalues는 명시 적 이동입니다 ( std::movexvalue를 반환 함).


11
@Thomas : 예입니다. 반환 값을 만드는 방법은 중요하지 않습니다. 중요한 것은를 반환한다는 것 &&입니다.
Nicol Bolas

1
참고 : prvalues는 방정식의 왼쪽에도있을 수 있습니다. X foo(); foo() = X;...이 근본적인 이유 때문에 위의 탁월한 대답을 끝까지 따라갈 수는 없습니다. lhs에있을 수 있다는 사실을 기반으로 새 xvalue 및 이전 스타일 prvalue
Dan Nissenbaum

1
X수업을받는 것; X foo();함수 선언이고 foo() = X();코드 줄입니다. ( foo() = X();위의 설명 에서 두 번째 괄호를 생략했습니다 .)이 사용법이 강조 표시된 상태에서 게시 한 질문에 대해서는 stackoverflow.com/questions/15482508/…
Dan Nissenbaum

1
@DanNissenbaum "xvalue는 대입 식의 왼쪽에있을 수 없습니다"-왜 안되나요?
Mikhail

1
깨달음의 대답. 이것은 의심 할 여지없이 가장 좋은 대답입니다. 그것은 새로운 가치 범주를 소개하고 이전에 일어난 일에 대한 이론적 근거를 제공했습니다.
Nikos

136

IMHO는 그 의미에 대한 최고의 설명으로 Stroustrup +는 Dániel SándorMohan의 예를 고려했습니다 .

Stroustrup :

지금 나는 심각하게 걱정했다. 분명히 우리는 곤경이나 혼란 또는 둘 다로 향했다. 나는 점심 시간을 분석하여 어떤 속성 (값)이 독립적인지 확인했습니다. 두 개의 독립적 인 속성 만있었습니다 :

  • has identity – 즉 주소, 포인터, 사용자는 두 사본이 동일한 지 여부를 결정할 수 있습니다.
  • can be moved from – 즉 우리는 불확실하지만 유효한 상태에서 "복사"의 출처로 남겨 둘 수 있습니다

이것은 정확히 세 종류의 가치가 있다는 결론으로 ​​이끌었습니다 (음수를 나타내는 데 대문자를 사용하는 정규 표현식 표기법을 사용하여 서둘러했습니다).

  • iM: 정체성을 가지고 있으며 이동할 수 없습니다
  • im: 동일성을 가지며 이동할 수 있음 (예 : lvalue를 rvalue 참조로 캐스트 한 결과)
  • Im: 신원이 없으며 이동할 수 있습니다.

    네 번째 가능성 IM은 (정체성이없고 움직일 수 없음 C++) 다른 언어 에서는 유용하지 않습니다 .

이 세 가지 기본 값 분류 외에도 두 가지 독립적 인 속성에 해당하는 두 가지 명백한 일반화가 있습니다.

  • i: 정체성이있다
  • m:에서 이동할 수 있습니다

이로 인해이 다이어그램을 칠판에 넣었습니다. 여기에 이미지 설명을 입력하십시오

명명

나는 우리에게 이름을 지정할 자유가 제한적이라는 것을 관찰했다 : 왼쪽에있는 두 지점 (라벨 iMi)은 형식이 다소간 사람들이 부르는 lvalues것이고 오른쪽에있는 두 점은 ( 형식 mIm)는 다소 형식적인 사람들이다. 전화했다 rvalues. 이것은 우리의 이름에 반영되어야합니다. 즉, 왼쪽의 "leg"는 W관련된 이름을 가져야 lvalue하고 오른쪽의 "leg"는 W관련된 이름을 가져야 rvalue.합니다.이 전체 토론 / 문제는 rvalue 참조 및 이동 의미론의 도입에서 발생합니다. 이 개념은 단순히 구성된 스트레이의 세계에 존재하지 않는 rvalueslvalues. 누군가는

  • 모두 valuelvalue또는rvalue
  • lvalue아니 어서 rvalue와는 rvalue아니다lvalue

우리의 의식, 매우 유용한 속성에 깊이 포함되어 있으며이 이분법의 흔적은 표준 초안에서 찾을 수 있습니다. 우리는 모두 그 속성을 보존하고 정확하게 만들어야한다는 데 동의했습니다. 이것은 우리의 명명 선택을 더욱 제한했습니다. 나는 표준 라이브러리 문구가 (일반화) rvalue를 의미하기 위해 사용하므로 표준 라이브러리 m의 기대와 텍스트를 보존하려면 오른쪽 하단 지점의 W이름을 지정해야합니다. rvalue.

이로 인해 명명에 대한 집중적 인 토론이 이루어졌습니다. 첫째, 우리는 결정에 필요한 lvalue.해야 lvalue평균 iM또는 일반화 i? Doug Gregor가 이끄는 핵심 언어 표현에서 단어 lvalue가 다른 단어 를 의미하는 장소를 나열했습니다 . 목록이 만들어졌으며 대부분의 경우 가장 까다 롭고 부서지기 쉬운 텍스트는 lvalue현재 의미 iM합니다. "이전에는"아무것도 움직이지 않았기 때문에 이것이 lvalue의 고전적인 의미입니다. move의 새로운 개념입니다 C++0x. 또한의 왼쪽 상단에 이름을 지정하면 W lvalue모든 값이 a lvalue또는 a rvalue이지만 둘 다가 아닌 속성이 제공됩니다.

따라서 Wis 의 왼쪽 상단 지점 lvalue과 오른쪽 하단 지점은 rvalue.무엇입니까? 왼쪽 하단 및 오른쪽 상단 지점은 무엇입니까? 왼쪽 하단은 고전적인 lvalue의 일반화로 움직일 수 있습니다. 그래서 그것은 generalized lvalue.우리는 그것을 명명했습니다 glvalue.. 약어에 대해 퀴즈를 풀 수 있지만 논리는 아닙니다. generalized lvalue 어쨌든 진지하게 사용 하면 어쨌든 축약 될 것이라고 가정 했기 때문에 즉시 사용하는 것이 더 좋았습니다 (또는 혼동 위험). W의 오른쪽 상단은 오른쪽 하단보다 덜 일반적입니다 (현재는이라고 함 rvalue). 이 지점은 이동할 수있는 객체의 원래 순수한 개념을 나타냅니다 (소멸자가 제외하고) 다시 참조 할 수 없기 때문입니다. 나는 specialized rvalue대조적으로 문구 를 좋아 generalized lvalue했지만pure rvalueprvalue이기는 것으로 축약 되었습니다 (그리고 아마도 그렇습니다). 따라서 W의 왼쪽 다리는 lvalue이고 glvalue오른쪽 다리는 prvalue이며 rvalue.우연히 모든 값은 glvalue 또는 prvalue이지만 둘다는 아닙니다.

이것은 W: 의 상단 중앙을 떠난다 : im; 즉, ID가 있고 이동할 수있는 값입니다. 우리에게는 난해한 짐승들에게 좋은 이름으로 인도 할만한 것이 없습니다. 그것들은 (초안) 표준 텍스트로 작업하는 사람들에게는 중요하지만 세대 이름이되지는 않을 것입니다. 우리는 우리를 안내하기위한 이름 지정에 대한 실제 제약 조건을 찾지 못했기 때문에 중심, 알 수없는, 이상한, xpert 만 또는 x 등급으로 'x'를 선택했습니다.

Steve는 최종 제품을 과시


14
예, C ++ 커밋에 대한 독창적 인 제안과 토론을 표준보다 읽는 것이 더 좋습니다. D
Ivan Kush

8
리터럴은 정체성이 없으며 이동할 수 없습니다. 그럼에도 불구하고 그들은 유용합니다.
DrPizza

나는 단지 일을 명확히하고 싶다. int && f () {return 1; } 및 MyClass && g () {return MyClass (); } xvalue를 반환, 그렇지? 그러면 표현식의 정체성을 어디에서 찾을 수 있습니까? f (); "g ();"? 리턴 문에 다른 표현식이 있기 때문에 식별과 동일합니다.이 표현식은 참조하는 것과 동일한 오브젝트를 나타냅니다. 올바르게 이해하고 있습니까?
Dániel Sándor

6
@DrPizza 표준에 따르면 : 문자열 리터럴은 lvalues이고 다른 모든 리터럴은 prvalues입니다. 엄밀히 말하면 문자열이 아닌 리터럴은 움직일 수 없어야한다고 주장 할 수는 있지만 표준이 작성된 방식은 아닙니다.
브라이언 반덴버그

59

소개

ISOC ++ 11 (공식적으로 ISO / IEC 14882 : 2011)은 C ++ 프로그래밍 언어 표준의 최신 버전입니다. 여기에는 몇 가지 새로운 기능과 개념이 포함되어 있습니다.

  • rvalue 참조
  • xvalue, glvalue, prvalue 표현식 값 범주
  • 의미론 이동

새로운 표현식 값 범주의 개념을 이해하려면 rvalue 및 lvalue 참조가 있음을 알고 있어야합니다. rvalue가 상수가 아닌 rvalue 참조로 전달 될 수 있다는 것을 아는 것이 좋습니다.

int& r_i=7; // compile error
int&& rr_i=7; // OK

작업 초안 N3337 (게시 된 ISOC ++ 11 표준과 가장 유사한 초안)에서 Lvalues ​​및 rvalues라는 하위 섹션을 인용하면 가치 범주 개념에 대한 직관을 얻을 수 있습니다.

3.10 L 값과 r 값 [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입니다. [예 : 반환 유형이
    참조가 아닌 함수를 호출 한 결과 는 prvalue입니다. 12, 7.3e5 또는
    true 와 같은 리터럴 값 도 prvalue입니다. — 끝 예제]

모든 표현은이 분류 체계의 기본 분류 중 하나 인 lvalue, xvalue 또는 prvalue에 속합니다. 식의이 속성을 값 범주라고합니다.

그러나 나는 "보통"이 실제로 일반적이지 않고 "평생의 끝이 가까움"이 실제로 구체적이지 않기 때문에이 하위 섹션이 개념을 명확하게 이해하기에 충분하다는 것을 확신하지 못한다. "예 : 반환 유형이 rvalue 참조 인 함수를 호출 한 결과는 xvalue입니다." 뱀이 꼬리를 물고있는 것처럼 들린다.

기본 가치 카테고리

모든 표현식은 정확히 하나의 기본 값 범주에 속합니다. 이러한 가치 범주는 lvalue, xvalue 및 prvalue 범주입니다.

l 값

E가 ALREADY에 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 범주 인 경우에만 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;
}

— 객체 표현식이 xvalue 인 비 참조 유형의 비 정적 데이터 멤버를 지정하는 클래스 멤버 액세스 표현식

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가 lvalue 또는 xvalue 범주에 속하지 않는 경우에만 표현식 E는 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 범주입니다.

rvalues

E가 xvalue 범주 또는 prvalue 범주에 속하는 경우에만 표현식 E가 rvalue 범주에 속합니다.

이 정의는 E가 E YET 외부에서 액세스 할 수 있도록하는 ID가없는 엔티티를 참조하는 경우에만 표현식 E가 rvalue 범주에 속함을 의미합니다.

glvalues

E가 lvalue 범주 또는 xvalue 범주에 속하는 경우에만 표현식 E는 glvalue 범주에 속합니다.

실제 규칙

Scott Meyer는 rvalue와 lvalue를 구별하기 위해 매우 유용한 경험 법칙을 발표 했습니다.

  • 식의 주소를 사용할 수 있으면 식은 lvalue입니다.
  • 표현식의 유형이 lvalue 참조 (예 : T & 또는 const T & 등) 인 경우 해당 표현식은 lvalue입니다.
  • 그렇지 않으면 표현식은 rvalue입니다. 개념적으로 (그리고 실제로도) rvalue는 함수에서 반환되거나 암시 적 형식 변환을 통해 생성 된 것과 같은 임시 개체에 해당합니다. 대부분의 리터럴 값 (예 : 10 및 5.3)도 rvalue입니다.

3
lvalue의 모든 예와 xvalue의 모든 예는 glvalue의 예이기도합니다. 편집 해 주셔서 감사합니다!
Dániel Sándor

1
네 말이 맞아 세 가지 주요 가치 범주가 충분합니다. Rvalue도 필요하지 않습니다. 편의상 rvalue와 glvalue가 표준이라고 생각합니다.
Dániel Sándor

1
변수 를 이해 struct As{void f(){this;}}하기 this가 어려웠던 것은 prvalue입니다. 나는 thislvalue이어야한다고 생각했습니다 . 표준 9.3.2가 말할 때까지 : 정적이 아닌 (9.3) 멤버 함수의 본문에서 키워드는 prvalue 표현식입니다.
r0ng

3
@ r0ng this는 prvalue이지만 *thislvalue입니다
Xeverous

1
"www" 주소가 항상 같은 것은 아닙니다 . 배열이므로 lvalue 입니다.
wally

35

C ++ 03의 범주는 rvalue 참조를 식 속성에 올바르게 도입하기에는 너무 제한적입니다.

그것들의 도입으로, 명명되지 않은 rvalue 참조는 rvalue로 평가되어 과부하 해결은 rvalue 참조 바인딩을 선호하므로 복사 생성자보다 이동 생성자를 선택할 수 있습니다. 그러나 이로 인해 동적 유형 및 자격과 같은 문제가 발생합니다 .

이것을 보여 주려면

int const&& f();

int main() {
  int &&i = f(); // disgusting!
}

X ++ 이전 드래프트에서는 C ++ 03에서 비 클래스 유형의 rvalue가 cv-qualified되지 않으므로 허용되었습니다. 그러나 constrvalue-reference 경우에 적용됩니다. 여기서 우리 객체 (= 메모리!)를 참조하고 클래스가 아닌 rvalue 에서 const를 삭제하는 것은 주로 객체가 없기 때문입니다.

동적 유형에 대한 문제는 본질적으로 유사합니다. C ++ 03에서 클래스 유형의 rvalue에는 알려진 동적 유형이 있으며 이는 해당 표현식의 정적 유형입니다. 다른 방법으로 사용하려면 lvalue로 평가되는 참조 또는 역 참조가 필요합니다. 명명되지 않은 rvalue 참조에는 해당되지 않지만 다형성 동작을 표시 할 수 있습니다. 문제를 해결하려면

  • 명명되지 않은 rvalue 참조는 xvalues됩니다 . 그들은 자격을 가질 수 있고 잠재적으로 동적 유형이 다를 수 있습니다. 의도 한 것처럼 오버로드 중에 rvalue 참조를 선호하며 비 상수 lvalue 참조에 바인딩하지 않습니다.

  • 이전에 rvalue (리터럴, 참조가 아닌 유형으로 캐스트하여 만든 객체)는 이제 prvalue가 됩니다. 오버로드 중 xvalue와 동일한 환경 설정이 있습니다.

  • 이전에 lvalue였던 것은 lvalue로 유지됩니다.

또한 정규화 될 수 있고 다른 동적 유형 ( glvalues )을 가질 수있는 그룹 과 과부하가 rvalue 참조 바인딩 ( rvalues )을 선호하는 그룹을 캡처하기 위해 두 그룹화가 수행됩니다 .


1
대답은 분명히 합리적입니다. xvalue는 cv-qualified 및 동적 형식화가 가능한 rvalue입니다!
리간드

26

나는 cppreference.com의 value 카테고리 설명을 만날 때까지 오랫동안 이것으로 어려움을 겪었습니다 .

실제로는 간단하지만 암기하기 어려운 방식으로 설명되는 경우가 많습니다. 여기에 매우 개략적으로 설명되어 있습니다. 페이지의 일부를 인용하겠습니다.

기본 카테고리

기본 값 범주는 두 가지 식 속성에 해당합니다.

  • 동일성 (identity) : 객체의 주소 또는 그들이 식별하는 기능 (직접 또는 간접적으로)을 비교하는 것과 같이, 표현이 다른 표현과 동일한 엔티티를 나타내는 지 여부를 결정할 수있다.

  • 에서 이동할 수 용구를 의미 표현식으로 결합 할 수 있다는 이동 이동 생성자 이동 할당 연산자 또는 다른 함수의 과부하 :.

다음과 같은 표현 :

  • 아이덴티티를 가지며 이동할 수없는 것을 lvalue 표현식 이라고 합니다 .
  • 동일성을 가지고 xvalue식이 라고 할 수 있습니다 .
  • 신원이없고 이동할 수있는 것을 prvalue식이 라고 합니다 .
  • 신원이 없으며 이동할 수 없습니다.

lvalue

좌변 ( "왼쪽 값") 표현식은 표현식 ID를 보유 하고 이동할 수 없다 .

rvalue (C ++ 11까지), prvalue (C ++ 11부터)

prvalue ( "순수를 rvalue")는 표현은 표현 의 정체성이없는이동할 수 있습니다 .

x 값

가 xValue 표현식은 표현식 ( "값 만료") ID를 보유 하고 이동할 수있다 .

glvalue

glvalue ( "일반 lvalue") 표현식은 lvalue 또는 xvalue 인 표현식입니다. 그것은 정체성이 있습니다. 이동하거나 이동하지 않을 수 있습니다.

rvalue (C ++ 11부터)

rvalue ( "올바른 값") 표현식은 prvalue 또는 xvalue 인 표현식입니다. 그것은 에서 이동할 수 있습니다 . 신원이있을 수도 있고 없을 수도 있습니다.


1
일부 책에서 xvalue는 x가 "전문가"또는 "예외"에서 온 것으로 표시됩니다.
noɥʇʎԀʎzɐɹƆ

더 중요한 것은 포괄적 인 예제 목록입니다.
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

19

이러한 새 범주는 기존 rvalue 및 lvalue 범주와 어떤 관련이 있습니까?

C ++ 03 lvalue는 여전히 C ++ 11 lvalue이지만 C ++ 03 rvalue는 C ++ 11에서 prvalue라고합니다.


14

Stroustrup을 읽은 후에도 rvalue / lvalue 구별을 이해했다고 생각한 후에도 위의 탁월한 답변에 대한 부록 하나가 혼란 스러웠습니다. 당신이 볼 때

int&& a = 3,

int&&형식 으로 읽고 arvalue 라고 결론 내리는 것은 매우 유혹적 입니다. 그렇지 않습니다 :

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?


1
이 답변이 명확해질 수 있다고 생각합니다. " a바인드가 허용되는 것": 물론, 2와 3 행에서 변수는 c & b이며, a가 아니며 바인딩되는 유형 a이 아니며 여기 에서 유형 이 관련이 없습니까? a선언 된 줄은 동일합니다 int a. 여기서 실제 주요 차이점은 1 행 const에서 3에 바인딩 할 필요가 없다는 것입니다.
Felix Dombek

12

이전 답변이 가치 범주 뒤에있는 이론을 철저히 다루었으므로 추가하고 싶은 또 다른 것이 있습니다. 실제로 놀고 테스트 할 수 있습니다.

값 범주에 대한 실습 실험의 경우 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))

이제 value category의 cppreference에서 거의 모든 예제를 재현 할 수 있습니다 .

다음은 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)); 

기본 카테고리를 파악하면 혼합 카테고리는 지루합니다.

더 많은 예제 (및 실험)를 보려면 컴파일러 탐색기 에서 다음 링크를 확인하십시오 . 그러나 어셈블리를 읽는 것을 귀찮게하지 마십시오. 모든 공통 컴파일러에서 작동하도록 많은 컴파일러를 추가했습니다.


#define IS_GLVALUE(X) IS_LVALUE(X) || IS_XVALUE(X)실제로 #define IS_GLVALUE(X) (IS_LVALUE(X) || IS_XVALUE(X))그렇지 않으면 &&두 사람이 어떻게되는지 살펴 봐야 한다고 생각 합니다 IS_GLVALUE.
Gabriel Devillers
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.