clang의 C ++ 11 status page 에서 "* r에 대한 rvalue reference"라는 제안을 보았습니다 .
rvalue 참조에 대해 꽤 많이 읽고 이해했지만 이것에 대해 알지 못한다고 생각합니다. 또한 용어를 사용하여 웹에서 많은 리소스를 찾을 수 없습니다.
페이지에 제안서에 대한 링크가 있습니다 : N2439 (이동 의미론을 * this로 확장), 그러나 거기에서도 많은 예제를 얻지 못했습니다.
이 기능은 무엇입니까?
clang의 C ++ 11 status page 에서 "* r에 대한 rvalue reference"라는 제안을 보았습니다 .
rvalue 참조에 대해 꽤 많이 읽고 이해했지만 이것에 대해 알지 못한다고 생각합니다. 또한 용어를 사용하여 웹에서 많은 리소스를 찾을 수 없습니다.
페이지에 제안서에 대한 링크가 있습니다 : N2439 (이동 의미론을 * this로 확장), 그러나 거기에서도 많은 예제를 얻지 못했습니다.
이 기능은 무엇입니까?
답변:
먼저, "* this의 한정자"는 "마케팅 명세서"일뿐입니다. *this변경되지 않는 유형은 이 게시물의 하단을 참조하십시오. 이 말로 이해하기가 훨씬 쉽습니다.
다음 코드 는 함수 † 의 "암시 적 객체 매개 변수"의 참조 한정자 를 기반으로 호출 할 함수를 선택합니다 .
// t.cpp
#include <iostream>
struct test{
void f() &{ std::cout << "lvalue object\n"; }
void f() &&{ std::cout << "rvalue object\n"; }
};
int main(){
test t;
t.f(); // lvalue
test().f(); // rvalue
}
산출:
$ clang++ -std=c++0x -stdlib=libc++ -Wall -pedantic t.cpp
$ ./a.out
lvalue object
rvalue object
함수가 호출되는 객체가 rvalue (예 : 이름이없는 임시) 인 경우를 활용하기 위해 모든 것이 수행됩니다. 추가 코드로 다음 코드를 사용하십시오.
struct test2{
std::unique_ptr<int[]> heavy_resource;
test2()
: heavy_resource(new int[500]) {}
operator std::unique_ptr<int[]>() const&{
// lvalue object, deep copy
std::unique_ptr<int[]> p(new int[500]);
for(int i=0; i < 500; ++i)
p[i] = heavy_resource[i];
return p;
}
operator std::unique_ptr<int[]>() &&{
// rvalue object
// we are garbage anyways, just move resource
return std::move(heavy_resource);
}
};
이것은 약간 생각할 수도 있지만 아이디어를 얻어야합니다.
당신이 결합 할 수 있습니다 이력서 - 예선 ( const와 volatile)과 REF-예선 ( &과 &&).
참고 : 여기에 많은 표준 인용문과 과부하 해결 설명이 있습니다!
† 이것이 어떻게 작동하고 @Nicol Bolas의 대답이 적어도 부분적으로 잘못된 이유를 이해하려면 C ++ 표준을 약간 파헤쳐 야합니다. 그것에 관심이 있습니다).
어떤 함수가 호출 될지는 과부하 해결 이라는 프로세스에 의해 결정됩니다 . 이 과정은 상당히 복잡하므로 중요한 비트 만 건 드리게됩니다.
먼저 멤버 함수의 오버로드 해상도가 어떻게 작동하는지 확인하는 것이 중요합니다.
§13.3.1 [over.match.funcs]
p2 후보 함수 세트는 동일한 인수 목록에 대해 분석 할 멤버 함수와 비 멤버 함수를 모두 포함 할 수 있습니다. 따라서 이기종 세트 내에서 인수 및 매개 변수 목록을 비교할 수 있도록 구성원 함수에는 암시 적 오브젝트 매개 변수라고하는 추가 매개 변수가있는 것으로 간주됩니다.이 매개 변수는 구성원 함수가 호출 된 오브젝트를 나타냅니다 . [...]
유사하게, 적절한 경우, 컨텍스트는 조작 될 오브젝트를 표시하기 위해 내재 된 오브젝트 인수 를 포함하는 인수리스트를 구성 할 수 있습니다 .
왜 멤버 함수와 비 멤버 함수를 비교해야합니까? 연산자 오버로드가 그 이유입니다. 이걸 고려하세요:
struct foo{
foo& operator<<(void*); // implementation unimportant
};
foo& operator<<(foo&, char const*); // implementation unimportant
다음과 같이 free 함수를 호출하고 싶습니까?
char const* s = "free foo!\n";
foo f;
f << s;
이것이 멤버 및 비 멤버 함수가 소위 오버로드 세트에 포함되는 이유입니다. 해상도를 덜 복잡하게하기 위해 표준 인용의 굵은 부분이 있습니다. 또한 이것은 우리에게 중요한 부분입니다 (동일한 조항).
p4 비 정적 멤버 함수의 경우 내재적 오브젝트 매개 변수의 유형은 다음과 같습니다.
"좌변에 참조 CV
X기능"은 선언하지 REF - 규정 하거나 함께&REF - 규정ref 한정자로 선언 된 함수의 경우 " cv에 대한 rvalue 참조
X"&&여기서
X함수는 멤버 인 클래스이고 cv 는 멤버 함수 선언의 cv 규정입니다. [...]p5 과부하 해결 중 [...] [t] 암시 적 객체 매개 변수 [...]는 해당 인수에 대한 변환이 다음 추가 규칙을 준수해야하므로 ID를 유지합니다.
내재적 오브젝트 매개 변수에 대한 인수를 보유하기 위해 임시 오브젝트를 도입 할 수 없습니다. 과
형식 일치를 위해 사용자 정의 변환을 적용 할 수 없습니다
[...]
(마지막 비트는 멤버 함수 (또는 연산자)가 호출되는 객체의 암시 적 변환을 기반으로 과부하 해결을 속일 수 없음을 의미합니다.)
이 게시물의 맨 위에있는 첫 번째 예를 살펴 보겠습니다. 위에서 언급 한 변환 후 오버로드 세트는 다음과 같습니다.
void f1(test&); // will only match lvalues, linked to 'void test::f() &'
void f2(test&&); // will only match rvalues, linked to 'void test::f() &&'
그런 다음 묵시적 객체 argument가 포함 된 인수 목록 이 오버로드 집합에 포함 된 모든 함수의 매개 변수 목록과 일치합니다. 이 경우 인수 목록에는 해당 객체 인수 만 포함됩니다. 어떻게 보이는지 봅시다 :
// first call to 'f' in 'main'
test t;
f1(t); // 't' (lvalue) can match 'test&' (lvalue reference)
// kept in overload-set
f2(t); // 't' not an rvalue, can't match 'test&&' (rvalue reference)
// taken out of overload-set
세트의 모든 과부하가 테스트 된 후에도 하나만 남아 있으면 과부하 해결에 성공하고 해당 변환 된 과부하에 연결된 함수가 호출됩니다. 'f'에 대한 두 번째 호출도 마찬가지입니다.
// second call to 'f' in 'main'
f1(test()); // 'test()' not an lvalue, can't match 'test&' (lvalue reference)
// taken out of overload-set
f2(test()); // 'test()' (rvalue) can match 'test&&' (rvalue reference)
// kept in overload-set
참고 그러나, 우리는 제공하지 않았다 REF-규정 즉, (와 같은 기능을 오버로드되지 않음) f1 것 를 rvalue을 (여전히 일치 §13.3.1) :
p5 [...] ref-qualifier 없이 선언 된 비 정적 멤버 함수의 경우 추가 규칙이 적용됩니다.
- 내재적 오브젝트 매개 변수가
const-qualified 가 아니더라도 다른 모든 측면에서 인수가 내재적 오브젝트 매개 변수의 유형으로 변환 될 수있는 한 rvalue는 매개 변수에 바인드 될 수 있습니다.
struct test{
void f() { std::cout << "lvalue or rvalue object\n"; }
};
int main(){
test t;
t.f(); // OK
test().f(); // OK too
}
이제 @Nicol의 대답이 부분적으로 잘못된 이유는 무엇입니까? 그는 말한다 :
이 선언은의 유형을 변경합니다
*this.
그것은 항상 잘못된 것 *this입니다 .
§5.3.1 [expr.unary.op] p1
단항
*연산자는 간접적으로 수행합니다 . 적용되는 표현식은 객체 유형에 대한 포인터 또는 함수 유형에 대한 포인터 여야하며 결과는 표현식이 가리키는 객체 또는 함수를 참조 하는 lvalue 입니다.
§9.3.2 [class.this] p1
비 정적 (9.3) 멤버 함수의 본문에서 키워드
this는 값이 함수가 호출되는 객체의 주소 인 prvalue 표현식입니다.this클래스 멤버 함수의 유형은X입니다X*. [...]
MyType(int a, double b) &&?
lvalue 참조 규정 자 양식에 대한 추가 사용 사례가 있습니다. C ++ 98에는 constrvalue 인 클래스 인스턴스에 멤버가 아닌 함수를 호출 할 수있는 언어가 있습니다 . 이것은 rvalueness의 개념에 반하는 모든 종류의 기묘함을 초래하고 내장 유형이 작동하는 방식에서 벗어납니다.
struct S {
S& operator ++();
S* operator &();
};
S() = S(); // rvalue as a left-hand-side of assignment!
S& foo = ++S(); // oops, dangling reference
&S(); // taking address of rvalue...
Lvalue 참조 한정자는 이러한 문제를 해결합니다.
struct S {
S& operator ++() &;
S* operator &() &;
const S& operator =(const S&) &;
};
이제 연산자는 기본 제공 유형의 연산자처럼 작동하며 lvalue 만 허용합니다.
이름과 서명이 같은 클래스에 두 개의 함수가 있다고 가정 해 봅시다. 그러나 그중 하나가 선언됩니다 const.
void SomeFunc() const;
void SomeFunc();
클래스 인스턴스 const가이 아닌 경우 과부하 해결은 우선적으로 비 const 버전을 선택합니다. 인스턴스가 const인 경우 사용자는 const버전 만 호출 할 수 있습니다 . 그리고 this포인터는const 포인터이므로 인스턴스를 변경할 수 없습니다.
"r- 값 참조"는 다른 대안을 추가 할 수있게합니다.
void RValueFunc() &&;
이를 통해 사용자가 적절한 r- 값을 통해 호출하는 경우 에만 호출 할 수있는 함수를 가질 수 있습니다 . 따라서 이것이 유형이라면 Object:
Object foo;
foo.RValueFunc(); //error: no `RValueFunc` version exists that takes `this` as l-value.
Object().RValueFunc(); //calls the non-const, && version.
이런 식으로 r- 값을 통해 객체에 액세스하는지 여부에 따라 동작을 특수화 할 수 있습니다.
r- 값 참조 버전과 비 참조 버전간에 과부하를 허용하지 않습니다. 즉, 멤버 함수 이름이 있으면 모든 버전에서에 l / r- 값 한정자를 사용하거나 사용 this하지 않습니다. 당신은 이것을 할 수 없습니다 :
void SomeFunc();
void SomeFunc() &&;
이 작업을 수행해야합니다.
void SomeFunc() &;
void SomeFunc() &&;
이 선언은의 유형을 변경합니다 *this. 이는 &&버전이 모두 r- 값 참조로 멤버에 액세스 함을 의미합니다 . 따라서 물체 내에서 쉽게 이동할 수 있습니다. 제안서의 첫 번째 버전에 제시된 예는 다음과 같습니다 (참고 : C ++ 11의 최종 버전에서는 다음 사항이 정확하지 않을 수 있습니다. 초기 "r-value from this"제안서와 동일).
class X {
std::vector<char> data_;
public:
// ...
std::vector<char> const & data() const & { return data_; }
std::vector<char> && data() && { return data_; }
};
X f();
// ...
X x;
std::vector<char> a = x.data(); // copy
std::vector<char> b = f().data(); // move
std::move두 번째 버전, 비? 또한 rvalue 참조가 왜 반환됩니까?
*this습니다. 그러나 혼란의 원인은 이해할 수 있습니다. 이는 ref 한정자가 오버로드 확인 및 함수 호출 중에 "this"(의도 한 목적으로 인용)! 개체가 바인딩되는 암시 적 (또는 "숨겨진") 함수 매개 변수 유형을 변경하기 때문입니다. 따라서 *thisXeo가 설명하는 것처럼 수정되었으므로 변경되지 않았습니다 . 대신 const함수 한정자가 만드는 것처럼 "hiddden"매개 변수를 변경하여 lvalue- 또는 rvalue-reference로 만듭니다 const.