먼저, "* 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 비 정적 멤버 함수의 경우 내재적 오브젝트 매개 변수의 유형은 다음과 같습니다.
여기서 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*
. [...]