Po pierwsze, „kwalifikatory odniesienia dla * tego” to tylko „oświadczenie marketingowe”. Rodzaj *this
nigdy się nie zmienia, patrz na dole tego postu. Jednak dzięki temu sformułowaniu o wiele łatwiej to zrozumieć.
Następnie poniższy kod wybiera funkcję, która ma zostać wywołana na podstawie kwalifikatora odniesienia „niejawnego parametru obiektu” funkcji † :
// 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
}
Wynik:
$ clang++ -std=c++0x -stdlib=libc++ -Wall -pedantic t.cpp
$ ./a.out
lvalue object
rvalue object
Wszystko po to, abyś mógł skorzystać z faktu, że obiekt, w którym funkcja jest wywoływana, to wartość (na przykład nienazwana tymczasowa). Weź następujący kod jako kolejny przykład:
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);
}
};
To może być nieco wymyślone, ale powinieneś o tym pomyśleć.
Pamiętaj, że możesz łączyć kwalifikatory cv ( const
i volatile
) i kwalifikatory ref ( &
i &&
).
Uwaga: tutaj znajduje się wiele standardowych cytatów i wyjaśnienie rozwiązania problemu przeciążenia!
† Aby zrozumieć, jak to działa i dlaczego odpowiedź @Nicol Bolas jest co najmniej częściowo błędna, musimy nieco zagłębić się w standard C ++ (część wyjaśniająca, dlaczego odpowiedź @ Nicol jest błędna, znajduje się na dole, jeśli jesteś tylko zainteresowani tym).
Która funkcja ma zostać wywołana, zależy od procesu zwanego rozdzielczością przeciążenia . Ten proces jest dość skomplikowany, więc dotkniemy tylko tego, co jest dla nas ważne.
Po pierwsze, ważne jest, aby zobaczyć, jak działa rozwiązywanie przeciążenia dla funkcji członka:
§13.3.1 [over.match.funcs]
p2 Zbiór funkcji kandydujących może zawierać zarówno funkcje składowe, jak i nie będące członkami, które zostaną rozstrzygnięte na tej samej liście argumentów. Aby listy argumentów i parametrów były porównywalne w ramach tego heterogenicznego zestawu, uważa się, że funkcja elementu zawiera dodatkowy parametr, zwany niejawnym parametrem obiektu, który reprezentuje obiekt, dla którego funkcja elementu została wywołana . [...]
p3 Podobnie, gdy jest to właściwe, kontekst może skonstruować listę argumentów, która zawiera domyślny argument obiektowy dla oznaczenia obiektu, który ma być obsługiwany.
Dlaczego musimy nawet porównywać funkcje składowe i nie będące członkami? Przeciążenie operatora, dlatego. Rozważ to:
struct foo{
foo& operator<<(void*); // implementation unimportant
};
foo& operator<<(foo&, char const*); // implementation unimportant
Na pewno chcesz, aby następujące wywołały funkcję bezpłatną, prawda?
char const* s = "free foo!\n";
foo f;
f << s;
Dlatego funkcje składowe i nie będące członkami są zawarte w tak zwanym zestawie przeciążenia. Aby uprościć rozdzielczość, istnieje odważna część standardowej oferty. Ponadto jest to dla nas ważny bit (ta sama klauzula):
p4 W przypadku niestatycznych funkcji składowych typem niejawnego parametru obiektu jest
gdzie X
jest klasą, której funkcja jest składnikiem, a cv jest kwalifikacją cv w deklaracji funkcji składowej. [...]
p5 Podczas rozwiązywania przeciążenia [...] [t] domyślny parametr obiektu [...] zachowuje swoją tożsamość, ponieważ konwersje na odpowiedni argument powinny być zgodne z następującymi dodatkowymi zasadami:
nie można wprowadzić żadnego obiektu tymczasowego do przechowywania argumentu dla parametru obiektu niejawnego; i
żadne konwersje zdefiniowane przez użytkownika nie mogą być zastosowane, aby osiągnąć dopasowanie typu do niego
[...]
(Ostatni bit oznacza po prostu, że nie można oszukiwać rozdzielczości przeciążenia na podstawie niejawnej konwersji obiektu, do którego wywoływana jest funkcja członka (lub operator).)
Weźmy pierwszy przykład na początku tego postu. Po wspomnianej transformacji zestaw przeciążeń wygląda mniej więcej tak:
void f1(test&); // will only match lvalues, linked to 'void test::f() &'
void f2(test&&); // will only match rvalues, linked to 'void test::f() &&'
Następnie lista argumentów zawierająca domyślny argument obiektowy jest porównywana z listą parametrów każdej funkcji zawartej w zestawie przeciążenia. W naszym przypadku lista argumentów będzie zawierać tylko ten argument obiektowy. Zobaczmy, jak to wygląda:
// 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
Jeśli po przetestowaniu wszystkich przeciążeń w zestawie pozostanie tylko jeden, rozwiązywanie problemu przeciążenia zakończyło się powodzeniem i wywoływana jest funkcja powiązana z tym transformowanym przeciążeniem. To samo dotyczy drugiego połączenia z „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
Uwaga jednak, że gdyby nie my dostarczyły żadnych Ref-kwalifikator (i jako takie nie przeciążony funkcji), że f1
byłoby dopasować rvalue (jeszcze §13.3.1
):
p5 [...] W przypadku niestatycznych funkcji składowych zadeklarowanych bez kwalifikatora odniesienia obowiązuje dodatkowa reguła:
- nawet jeśli niejawny parametr obiektu nie
const
jest kwalifikowany, wartość można powiązać z parametrem, o ile pod wszystkimi innymi względami argument można przekonwertować na typ parametru obiektu niejawnego.
struct test{
void f() { std::cout << "lvalue or rvalue object\n"; }
};
int main(){
test t;
t.f(); // OK
test().f(); // OK too
}
Teraz, dlaczego odpowiedź @ Nicol jest przynajmniej częściowo błędna. On mówi:
Uwaga: ta deklaracja zmienia typ *this
.
To źle, zawsze*this
jest to wartość:
§5.3.1 [expr.unary.op] p1
Jednoargumentowy *
operator wykonuje pośrednie : wyrażenie, do którego jest stosowane, powinno być wskaźnikiem do typu obiektu lub wskaźnikiem do typu funkcji, a wynikiem jest wartość odnosząca się do obiektu lub funkcji, na którą wskazuje wyrażenie.
§9.3.2 [class.this] p1
W treści niestatystycznej (9,3) funkcji this
składowej słowo kluczowe jest wyrażeniem prvalue, którego wartością jest adres obiektu, dla którego funkcja jest wywoływana. Typem this
funkcji składowej klasy X
jest X*
. [...]