Jika saya memiliki dua variabel anggota konstan yang berbeda, yang keduanya perlu diinisialisasi berdasarkan pemanggilan fungsi yang sama, apakah ada cara untuk melakukan ini tanpa memanggil fungsi dua kali?
Misalnya, kelas pecahan di mana pembilang dan penyebutnya konstan.
int gcd(int a, int b); // Greatest Common Divisor
class Fraction {
public:
// Lets say we want to initialize to a reduced fraction
Fraction(int a, int b) : numerator(a/gcd(a,b)), denominator(b/gcd(a,b))
{
}
private:
const int numerator, denominator;
};
Ini menghasilkan waktu yang terbuang, karena fungsi GCD disebut dua kali. Anda juga dapat mendefinisikan anggota kelas baru gcd_a_b
,, dan pertama-tama menetapkan output gcd ke yang ada dalam daftar penginisialisasi, tetapi kemudian ini akan menyebabkan memori yang terbuang.
Secara umum, apakah ada cara untuk melakukan ini tanpa panggilan fungsi atau memori yang terbuang? Bisakah Anda membuat variabel sementara dalam daftar penginisialisasi? Terima kasih.
-O3
. Tapi mungkin untuk implementasi tes sederhana itu sebenarnya akan sebaris dengan panggilan fungsi. Jika Anda menggunakan__attribute__((const))
atau murni pada prototipe tanpa memberikan definisi yang terlihat, itu harus membiarkan GCC atau dentang melakukan eliminasi umum-sub-ekspresi (CSE) antara dua panggilan dengan arg yang sama. Perhatikan bahwa jawaban Drew bekerja bahkan untuk fungsi non-murni sehingga jauh lebih baik dan Anda harus menggunakannya kapan saja func mungkin tidak sebaris.Jawaban:
Iya. Ini dapat dilakukan dengan konstruktor pendelegasian , diperkenalkan pada C ++ 11.
Sebuah konstruktor mendelegasikan adalah cara yang sangat efisien untuk memperoleh nilai sementara yang dibutuhkan untuk pembangunan sebelum setiap variabel anggota diinisialisasi.
sumber
.h
), bahkan jika definisi konstruktor yang sebenarnya tidak terlihat untuk inlining. yaitugcd()
panggilan akan inline ke masing-masing callsite konstruktor, dan menyerahkan hanyacall
kepada konstruktor pribadi 3-operan.Vars anggota diinisialisasi oleh urutan mereka dinyatakan dalam deklerasi kelas, maka Anda dapat melakukan hal berikut (secara matematis)
Tidak perlu memanggil konstruktor lain atau bahkan membuatnya.
sumber
Fraction(a,b,gcd(a,b))
mengarahkan delegasi ke penelepon, yang mengarah ke total biaya yang lebih sedikit. Inlining itu lebih mudah dilakukan oleh kompiler daripada membatalkan pembagian tambahan dalam hal ini. Saya tidak mencobanya di godbolt.org tetapi Anda bisa jika Anda penasaran. Gunakan gcc atau dentang-O3
seperti yang biasa digunakan build. (C ++ dirancang berdasarkan asumsi kompiler optimisasi modern, maka fitur seperticonstexpr
)@Drew Dormann memberikan solusi yang mirip dengan yang ada dalam pikiran saya. Karena OP tidak pernah menyebutkan tidak dapat memodifikasi ctor, ini dapat disebut dengan
Fraction f {a, b, gcd(a, b)}
:Hanya dengan cara ini tidak ada panggilan kedua ke fungsi, konstruktor atau sebaliknya, jadi tidak ada waktu yang terbuang. Dan itu bukan memori yang sia-sia karena sementara harus dibuat pula, jadi Anda sebaiknya memanfaatkannya. Itu juga menghindari pembagian ekstra.
sumber
const
, tetapi setidaknya berfungsi untuk jenis lainnya. Dan pembagian ekstra apa yang Anda "juga" hindari? Maksudmu vs jawaban asmmo?,gcd(foo, bar)
adalah kode tambahan yang bisa dan karenanya harus difaktorkan dari setiap ruang info di sumber . Itu masalah pemeliharaan / keterbacaan, bukan kinerja. Kompiler kemungkinan besar akan mengikutinya pada waktu kompilasi, yang Anda inginkan untuk kinerja.Fraction f( x+y, a+b );
Untuk menuliskannya dengan cara Anda, Anda harus menulisBadFraction f( x+y, a+b, gcd(x+y, a+b) );
atau menggunakan tmp vars. Atau bahkan lebih buruk, bagaimana jika Anda ingin menulisFraction f( foo(x), bar(y) );
- maka Anda akan memerlukan situs panggilan untuk mendeklarasikan beberapa tmp vars untuk menyimpan nilai kembali, atau memanggil fungsi-fungsi itu lagi dan berharap kompiler CSE mereka pergi, yang mana kami hindari. Apakah Anda ingin men-debug kasus satu pemanggil yang menggabungkan args kegcd
sehingga sebenarnya bukan GCD dari 2 args pertama yang diteruskan ke konstruktor? Tidak? Maka jangan membuat bug itu mungkin.