Pertama, "ref-kualifikasi untuk * ini" hanyalah "pernyataan pemasaran". Jenis *this
tidak pernah berubah, lihat bagian bawah posting ini. Akan lebih mudah untuk memahaminya dengan kata-kata ini.
Selanjutnya, kode berikut memilih fungsi yang akan dipanggil berdasarkan kualifikasi -ulang dari "parameter objek implisit" dari fungsi † :
// 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
}
Keluaran:
$ clang++ -std=c++0x -stdlib=libc++ -Wall -pedantic t.cpp
$ ./a.out
lvalue object
rvalue object
Semuanya dilakukan untuk memungkinkan Anda mengambil keuntungan dari fakta ketika objek fungsi dipanggil adalah nilai (tidak disebutkan namanya sementara, misalnya). Ambil kode berikut sebagai contoh lebih lanjut:
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);
}
};
Ini mungkin sedikit dibuat-buat, tetapi Anda harus mendapatkan ide.
Perhatikan bahwa Anda dapat menggabungkan kualifikasi-cv ( const
dan volatile
) dan kualifikasi-ref ( &
dan &&
).
Catatan: Banyak kutipan standar dan penjelasan resolusi yang berlebihan setelah di sini!
† Untuk memahami bagaimana ini bekerja, dan mengapa jawaban @Nicol Bolas setidaknya sebagian salah, kita harus menggali sedikit dalam standar C ++ (bagian yang menjelaskan mengapa jawaban @ Nicol salah ada di bagian bawah, jika Anda hanya tertarik pada itu).
Fungsi mana yang akan dipanggil ditentukan oleh proses yang disebut resolusi kelebihan beban . Proses ini cukup rumit, jadi kami hanya akan menyentuh bit yang penting bagi kami.
Pertama, penting untuk melihat bagaimana resolusi kelebihan untuk fungsi anggota bekerja:
§13.3.1 [over.match.funcs]
p2 Himpunan fungsi kandidat dapat berisi fungsi anggota dan non-anggota untuk diselesaikan terhadap daftar argumen yang sama. Sehingga daftar argumen dan parameter sebanding dalam set heterogen ini, , fungsi anggota dianggap memiliki parameter tambahan, disebut parameter objek implisit, yang mewakili objek yang fungsi anggotanya disebut . [...]
p3 Demikian pula, bila sesuai, konteksnya dapat membuat daftar argumen yang berisi argumen objek tersirat untuk menunjukkan objek yang akan dioperasikan.
Mengapa kita bahkan perlu membandingkan fungsi anggota dan non-anggota? Operator kelebihan beban, itu sebabnya. Pertimbangkan ini:
struct foo{
foo& operator<<(void*); // implementation unimportant
};
foo& operator<<(foo&, char const*); // implementation unimportant
Anda tentu ingin yang berikut memanggil fungsi gratis, bukan?
char const* s = "free foo!\n";
foo f;
f << s;
Itu sebabnya fungsi anggota dan non-anggota termasuk dalam yang disebut overload-set. Untuk membuat resolusi kurang rumit, ada bagian tebal dari kutipan standar. Selain itu, ini adalah bagian penting bagi kami (klausa yang sama):
p4 Untuk fungsi anggota non-statis, tipe parameter objek implisit adalah
di mana X
kelas yang fungsinya adalah anggota dan cv adalah kualifikasi-cv pada deklarasi fungsi anggota. [...]
p5 Selama resolusi kelebihan [[]] [t] ia parameter objek implisit [...] mempertahankan identitasnya karena konversi pada argumen terkait harus mematuhi aturan tambahan ini:
tidak ada objek sementara dapat diperkenalkan untuk memegang argumen untuk parameter objek implisit; dan
tidak ada konversi yang ditentukan pengguna dapat diterapkan untuk mencapai jenis yang cocok dengannya
[...]
(Bit terakhir hanya berarti bahwa Anda tidak dapat menipu resolusi kelebihan beban berdasarkan konversi implisit objek fungsi anggota (atau operator) dipanggil.)
Mari kita ambil contoh pertama di bagian atas posting ini. Setelah transformasi yang disebutkan di atas, set overload terlihat seperti ini:
void f1(test&); // will only match lvalues, linked to 'void test::f() &'
void f2(test&&); // will only match rvalues, linked to 'void test::f() &&'
Kemudian daftar argumen, yang berisi argumen objek tersirat , dicocokkan dengan daftar parameter dari setiap fungsi yang terkandung dalam set-overload. Dalam kasus kami, daftar argumen hanya akan berisi argumen objek itu. Mari kita lihat bagaimana tampilannya:
// 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
Jika, setelah semua kelebihan dalam set diuji, hanya satu yang tersisa, resolusi kelebihan berhasil dan fungsi yang terkait dengan kelebihan beban yang ditransformasikan disebut. Hal yang sama berlaku untuk panggilan kedua ke '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
Namun perlu dicatat bahwa, jika kami tidak menyediakan ref-kualifikasi apa pun (dan karena itu tidak membebani fungsi), itu f1
akan cocok dengan nilai (masih §13.3.1
):
p5 [...] Untuk fungsi anggota non-statis yang dideklarasikan tanpa kualifikasi-ref , aturan tambahan berlaku:
- bahkan jika parameter objek implisit tidak
const
dikualifikasi, nilai dapat diikat ke parameter selama semua hal lain argumen dapat dikonversi ke jenis parameter objek implisit.
struct test{
void f() { std::cout << "lvalue or rvalue object\n"; }
};
int main(){
test t;
t.f(); // OK
test().f(); // OK too
}
Sekarang, mengapa @ Nicol menjawab setidaknya sebagian salah. Dia berkata:
Perhatikan bahwa deklarasi ini mengubah tipe *this
.
Itu salah, selalu*this
bernilai tinggi :
§5.3.1 [expr.unary.op] p1
*
Operator unary melakukan tipuan : ekspresi yang diterapkan harus menjadi pointer ke tipe objek, atau pointer ke tipe fungsi dan hasilnya adalah nilai yang mengacu pada objek atau fungsi yang menjadi titik ekspresi.
§9.3.2 [class.this] p1
Dalam isi fungsi anggota yang tidak statis (9,3), kata kunci this
adalah ekspresi nilai awal yang nilainya adalah alamat objek yang dipanggil untuk fungsi tersebut. Jenis this
dalam fungsi anggota kelas X
adalah X*
. [...]