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*
. [...]
MyType(int a, double b) &&
:?Ada kasus penggunaan tambahan untuk formulir kualifikasi ulang lvalue. C ++ 98 memiliki bahasa yang memungkinkan
const
fungsi non- anggota dipanggil untuk instance kelas yang merupakan nilai. Ini mengarah ke semua jenis keanehan yang bertentangan dengan konsep rvalueness dan menyimpang dari cara kerja tipe bawaan:Lvalue ref-kualifikasi menyelesaikan masalah ini:
Sekarang operator bekerja seperti orang-orang dari tipe builtin, hanya menerima nilai.
sumber
Katakanlah Anda memiliki dua fungsi di kelas, keduanya dengan nama dan tanda tangan yang sama. Tapi salah satunya dinyatakan
const
:Jika instance kelas tidak
const
, resolusi kelebihan akan memilih versi non-const. Jika instansinya adalahconst
, pengguna hanya dapat memanggilconst
versi. Danthis
penunjuknya adalah aconst
pointer, jadi instance tidak dapat diubah.Apa yang dilakukan r-value reference untuk ini adalah memungkinkan Anda menambahkan alternatif lain:
Ini memungkinkan Anda untuk memiliki fungsi yang hanya bisa dipanggil jika pengguna memanggilnya melalui nilai-r yang tepat. Jadi jika ini termasuk dalam jenis
Object
:Dengan cara ini, Anda dapat mengkhususkan perilaku berdasarkan pada apakah objek sedang diakses melalui nilai-r atau tidak.
Perhatikan bahwa Anda tidak diizinkan untuk membebani antara versi referensi r-nilai dan versi non-referensi. Artinya, jika Anda memiliki nama fungsi anggota, semua versinya menggunakan kualifikasi nilai l / r
this
, atau tidak ada yang memilikinya. Anda tidak dapat melakukan ini:Anda harus melakukan ini:
Perhatikan bahwa deklarasi ini mengubah tipe
*this
. Ini berarti bahwa&&
versi semua anggota akses sebagai r-nilai referensi. Jadi menjadi mungkin untuk dengan mudah bergerak dari dalam objek. Contoh yang diberikan dalam versi pertama proposal adalah (catatan: yang berikut ini mungkin tidak benar dengan versi final C ++ 11; langsung dari proposal "r-value from this" awal):sumber
std::move
versi kedua, bukan? Juga, mengapa referensi rvalue kembali?*this
, namun saya bisa mengerti dari mana kebingungan itu berasal. Ini karena ref-kualifikasi mengubah jenis parameter fungsi implisit (atau "tersembunyi") yang objek "ini" (tanda kutip maksudkan di sini!) Terikat selama resolusi kelebihan dan pemanggilan fungsi. Jadi, tidak ada perubahan*this
sejak ini diperbaiki seperti yang dijelaskan Xeo. Alih-alih mengubah "hiddden" parameter untuk menjadikannya sebagai nilai-atau nilai-referensi, sama seperticonst
fungsi kualifikasi membuatnyaconst
dll.