Berapa banyak data yang disalin, ketika mengembalikan std :: vector dalam sebuah fungsi dan seberapa besar optimasi akan menempatkan std :: vector di free-store (di heap) dan mengembalikan pointer yaitu:
std::vector *f()
{
std::vector *result = new std::vector();
/*
Insert elements into result
*/
return result;
}
lebih efisien daripada:
std::vector f()
{
std::vector result;
/*
Insert elements into result
*/
return result;
}
?
c++
return-value
stdvector
Morten
sumber
sumber
f
?Jawaban:
Di C ++ 11, ini adalah cara yang disukai:
Artinya, kembali dengan nilai.
Dengan C ++ 11,
std::vector
memiliki semantik bergerak, yang berarti vektor lokal yang dideklarasikan dalam fungsi Anda akan dipindahkan saat kembali dan dalam beberapa kasus bahkan pemindahan tersebut dapat dieliminasi oleh kompilator.sumber
return std::move(v);
akan menonaktifkan move-elision meski pun bisa dengan hanyareturn v;
. Jadi yang terakhir lebih disukai.Anda harus kembali dengan nilai.
Standar memiliki fitur khusus untuk meningkatkan efisiensi pengembalian berdasarkan nilai. Ini disebut "penghapusan salinan", dan lebih khusus lagi dalam hal ini "bernama optimasi nilai pengembalian (NRVO)".
Compiler tidak harus mengimplementasikannya, tetapi compiler tidak harus mengimplementasikan function inlining (atau melakukan optimasi sama sekali). Tetapi kinerja pustaka standar bisa sangat buruk jika kompiler tidak dioptimalkan, dan semua kompiler serius menerapkan inlining dan NRVO (dan pengoptimalan lainnya).
Ketika NRVO diterapkan, tidak akan ada penyalinan pada kode berikut:
Tetapi pengguna mungkin ingin melakukan ini:
Penghapusan salinan tidak mencegah penyalinan di sini karena ini adalah tugas daripada inisialisasi. Namun, Anda tetap harus mengembalikan nilai. Di C ++ 11, tugas dioptimalkan oleh sesuatu yang berbeda, yang disebut "semantik bergerak". Di C ++ 03, kode di atas memang menyebabkan salinan, dan meskipun secara teori pengoptimal mungkin dapat menghindarinya, dalam praktiknya terlalu sulit. Jadi alih-alih
myvec = f()
, di C ++ 03 Anda harus menulis ini:Ada opsi lain, yaitu menawarkan antarmuka yang lebih fleksibel kepada pengguna:
Anda juga dapat mendukung antarmuka berbasis vektor yang ada selain itu:
Ini mungkin kurang efisien daripada kode yang ada, jika kode yang ada digunakan
reserve()
dengan cara yang lebih kompleks daripada jumlah tetap di muka. Tetapi jika kode Anda yang ada pada dasarnya memanggilpush_back
vektor berulang kali, maka kode berbasis template ini seharusnya sama baiknya.sumber
Sudah waktunya saya memposting jawaban tentang RVO , saya juga ...
Jika Anda mengembalikan objek berdasarkan nilai, kompilator sering kali mengoptimalkannya sehingga tidak dibangun dua kali, karena akan berlebihan jika membuatnya dalam fungsi sebagai sementara lalu menyalinnya. Ini disebut pengoptimalan nilai pengembalian: objek yang dibuat akan dipindahkan alih-alih disalin.
sumber
Idiom pra-C ++ 11 yang umum adalah meneruskan referensi ke objek yang sedang diisi.
Maka tidak ada penyalinan vektor.
sumber
Jika compiler mendukung Named Return Value Optimization ( http://msdn.microsoft.com/en-us/library/ms364057(v=vs.80).aspx ), Anda dapat langsung mengembalikan vektor asalkan tidak ada:
NRVO mengoptimalkan pemanggilan konstruktor salinan dan destruktor yang berlebihan dan dengan demikian meningkatkan kinerja secara keseluruhan.
Seharusnya tidak ada perbedaan nyata dalam contoh Anda.
sumber
Dan jika Anda ingin mencetaknya di main () Anda harus melakukannya dalam satu putaran.
sumber
Betapapun bagusnya "pengembalian berdasarkan nilai", ini adalah jenis kode yang dapat menyebabkan kesalahan. Pertimbangkan program berikut:
Program yang salah di atas tidak akan menunjukkan kesalahan meskipun seseorang menggunakan opsi pelaporan GNU g ++ -Wall -Wextra -Weffc ++
Jika Anda harus menghasilkan nilai, hal berikut akan berfungsi sebagai pengganti pemanggilan vecFunc () dua kali:
Hal di atas juga tidak menghasilkan objek anonim selama iterasi loop, tetapi memerlukan operasi penyalinan yang mungkin (yang, seperti beberapa catatan, mungkin dioptimalkan dalam beberapa keadaan. Tetapi metode referensi menjamin bahwa tidak ada salinan yang akan dihasilkan. Percaya kompiler akan melakukan RVO bukanlah pengganti untuk mencoba membangun kode paling efisien yang Anda bisa.Jika Anda dapat memperdebatkan kebutuhan kompiler untuk melakukan RVO, Anda berada di depan permainan.
sumber
sumber