Apakah mengembalikan dengan referensi nilai lebih efisien?

133

sebagai contoh:

Beta_ab&&
Beta::toAB() const {
    return move(Beta_ab(1, 1));
}
Neil G
sumber

Jawaban:

246
Beta_ab&&
Beta::toAB() const {
    return move(Beta_ab(1, 1));
}

Ini mengembalikan referensi menggantung, seperti halnya dengan kasus referensi lvalue. Setelah fungsi kembali, objek sementara akan dihancurkan. Anda harus mengembalikan Beta_abberdasarkan nilainya, seperti berikut ini

Beta_ab
Beta::toAB() const {
    return Beta_ab(1, 1);
}

Sekarang, itu benar memindahkan Beta_abobjek sementara ke nilai kembali fungsi. Jika kompiler bisa, itu akan menghindari langkah sama sekali, dengan menggunakan RVO (optimasi nilai balik). Sekarang, Anda dapat melakukan hal berikut

Beta_ab ab = others.toAB();

Dan itu akan memindahkan konstruksi sementara ke ab, atau melakukan RVO untuk menghilangkan melakukan gerakan atau menyalin sama sekali. Saya sarankan Anda membaca BoostCon09 Rvalue Referensi 101 yang menjelaskan masalah ini, dan bagaimana (N) RVO berinteraksi dengan ini.


Kasus Anda mengembalikan referensi nilai akan menjadi ide bagus di kesempatan lain. Bayangkan Anda memiliki getAB()fungsi yang sering Anda panggil untuk sementara waktu. Tidak optimal untuk membuatnya mengembalikan referensi nilai konstan untuk nilai sementara. Anda dapat menerapkannya seperti ini

struct Beta {
  Beta_ab ab;
  Beta_ab const& getAB() const& { return ab; }
  Beta_ab && getAB() && { return move(ab); }
};

Perhatikan bahwa movedalam hal ini bukan opsional, karena abbukan merupakan nilai lokal otomatis atau sementara. Sekarang, ref-kualifikasi && mengatakan bahwa fungsi kedua dipanggil pada nilai sementara, membuat langkah berikut, alih-alih menyalin

Beta_ab ab = Beta().getAB();
Johannes Schaub - litb
sumber
51
Saya selalu mengasumsikan masalah referensi menggantung pergi secara otomatis ketika tipe kembali adalah referensi r-nilai. Senang saya mendapatkan itu lurus sebelum itu menggigit saya. Stack smashing bugs menghisap.
deft_code
31
:) Sungguh, referensi nilai adalah "hanya referensi" seperti referensi nilai. mereka tidak menyalin atau menyimpan apa pun.
Johannes Schaub - litb
9
Apa fungsi const & kualifikasi pada fungsi anggota lebih dari sekadar const?
galinette
4
+1 tautan, tetapi tautan rusak: BoostCon09 Rvalue Referensi 101
Siu Ching Pong -Asuka Kenji-
3
@galinette Ini adalah ref-kualifikasi .
Malcolm
2

Ini bisa lebih efisien, misalnya, dalam konteks yang sedikit berbeda:

template <typename T>
T&& min_(T&& a, T &&b) {
    return std::move(a < b? a: b);
}

int main() {
   const std::string s = min_(std::string("A"), std::string("B"));
   fprintf(stderr, "min: %s\n", s.c_str());
   return 0;
}

Sebagai pengamatan yang menarik, pada mesin saya clang++ -O3menghasilkan 54 instruksi untuk kode di atas dibandingkan 62 instruksi untuk reguler std::min. Namun, dengan -O0itu menghasilkan 518 instruksi untuk kode di atas dibandingkan 481 untuk reguler std::min.

wonder.mice
sumber
Saya bingung dengan jawaban Anda. Pernah mencoba versi yang serupa (mungkin) tetapi gagal: ideone.com/4GyUbZ Bisakah Anda menjelaskan alasannya?
Deqing
Anda menggunakan referensi pada objek sementara di for(:)dan mengintegrasikan lebih dari objek yang tidak dialokasikan. Fix: ideone.com/tQVOal
wonder.mice
3
bukankah jawaban ini salah? untuk parameter templat T, T&& bukan referensi r-nilai, melainkan referensi universal, dan untuk kasus itu, kita harus selalu memanggil std :: forward <T>, bukan std :: move! Belum lagi, bahwa jawaban ini bertentangan langsung dengan yang terpilih di atas.
xdavidliu
@xdavidliu jawaban ini adalah contoh yang dibuat bagaimana mengembalikan dengan nilai bisa lebih efisien. std::move()hanya digunakan sebagai pemeran eksplisit untuk menggambarkan poin lebih jelas. Ini bukan kode yang akan Anda salin-tempel ke proyek Anda. Itu tidak bertentangan dengan jawaban terpilih, karena ada objek sementara dibuat di dalam fungsi. Di sini objek yang dikembalikan adalah salah satu argumen (objek sementara dihancurkan sebagai langkah terakhir dalam mengevaluasi ekspresi penuh yang (secara leksikal) berisi titik di mana mereka dibuat).
wonder.mice
2
@ wonder.mice maka silakan ganti T dengan std :: string; tidak perlu menggunakan template sama sekali di sini, dan menggunakan T&& sebagai referensi nilai-r adalah gaya yang mengerikan yang secara tidak perlu membingungkan orang-orang yang baru mengenal template dan nilai-r.
xdavidliu