Pemahaman saya adalah bahwa dalam C ++ 11, ketika Anda mengembalikan variabel lokal dari suatu fungsi dengan nilai, kompiler diperbolehkan untuk memperlakukan variabel itu sebagai r-nilai referensi dan 'memindahkannya keluar dari fungsi untuk mengembalikannya (jika RVO / NRVO tidak terjadi sebagai gantinya, tentu saja).
Pertanyaan saya adalah, tidak bisakah ini memecahkan kode yang ada?
Pertimbangkan kode berikut:
#include <iostream>
#include <string>
struct bar
{
bar(const std::string& str) : _str(str) {}
bar(const bar&) = delete;
bar(bar&& other) : _str(std::move(other._str)) {other._str = "Stolen";}
void print() {std::cout << _str << std::endl;}
std::string _str;
};
struct foo
{
foo(bar& b) : _b(b) {}
~foo() {_b.print();}
bar& _b;
};
bar foobar()
{
bar b("Hello, World!");
foo f(b);
return std::move(b);
}
int main()
{
foobar();
return EXIT_SUCCESS;
}
Pikiranku adalah bahwa akan mungkin bagi penghancur objek lokal untuk referensi objek yang secara implisit dipindahkan, dan karena itu secara tak terduga melihat objek 'kosong'. Saya mencoba menguji ini (lihat http://ideone.com/ZURoeT ), tetapi saya mendapatkan hasil 'benar' tanpa eksplisit std::move
dalam foobar()
. Saya menduga itu karena NRVO, tetapi saya tidak mencoba mengatur ulang kode untuk menonaktifkannya.
Apakah saya benar karena transformasi ini (menyebabkan perpindahan fungsi) terjadi secara implisit dan dapat memecah kode yang ada?
PEMBARUAN Berikut adalah contoh yang menggambarkan apa yang saya bicarakan. Dua tautan berikut adalah untuk kode yang sama. http://ideone.com/4GFIRu - C ++ 03 http://ideone.com/FcL2Xj - C ++ 11
Jika Anda melihat outputnya, itu berbeda.
Jadi, saya kira pertanyaan ini sekarang menjadi, apakah ini dipertimbangkan ketika menambahkan langkah implisit ke standar, dan diputuskan bahwa tidak apa-apa untuk menambahkan perubahan ini karena kode semacam ini cukup langka? Saya juga bertanya-tanya apakah ada kompiler akan memperingatkan dalam kasus-kasus seperti ini ...
Jawaban:
Scott Meyers memposting ke comp.lang.c ++ (Agustus 2010) tentang masalah di mana generasi konstruktor pemindahan implisit dapat memecahkan invarian kelas C ++ 03:
Di sini masalahnya adalah bahwa dalam C ++ 03,
X
memiliki invarian yangv
anggotanya selalu memiliki 5 elemen.X::~X()
mengandalkan invarian itu, tetapi konstruktor bergerak yang baru diperkenalkan pindah dariv
, sehingga menetapkan panjangnya ke nol.Ini terkait dengan contoh Anda karena invarian rusak hanya terdeteksi di
X
destruktor (seperti yang Anda katakan itu mungkin untuk destruktor objek lokal untuk referensi objek yang dipindahkan secara implisit, dan karena itu secara tak terduga melihat objek kosong ).C ++ 11 mencoba mencapai keseimbangan antara memecah beberapa kode yang ada dan memberikan optimisasi bermanfaat berdasarkan move constructor.
Komite awalnya memutuskan bahwa memindahkan konstruktor dan memindahkan operator penugasan harus dihasilkan oleh kompiler ketika tidak disediakan oleh pengguna.
Kemudian memutuskan bahwa ini memang memprihatinkan dan membatasi generasi otomatis konstruktor pemindahan dan memindahkan operator penugasan sedemikian rupa sehingga jauh lebih kecil kemungkinannya, walaupun bukan tidak mungkin, untuk memecahkan kode (misalnya destruktor yang didefinisikan secara eksplisit).
Sangat menggoda untuk berpikir bahwa mencegah pembuatan konstruktor pemindahan implisit ketika ada pemecah yang ditentukan pengguna sudah cukup tetapi itu tidak benar ( N3153 - Pemindahan Tersirat Harus Pergi untuk perincian lebih lanjut).
Di N3174 - Untuk Memindahkan atau tidak Memindahkan Stroupstrup mengatakan:
sumber