Untuk menghindari duplikasi yang terkait dengan konstanta C ++ non-sepele, apakah ada kasus di mana const_cast akan bekerja tetapi fungsi const pribadi yang mengembalikan non-const tidak?
Dalam Scott Meyers ' C ++ 3 item Efektif , ia menyarankan bahwa const_cast dikombinasikan dengan pemain statis bisa menjadi cara yang efektif dan aman untuk menghindari kode duplikat, misalnya
const void* Bar::bar(int i) const
{
...
return variableResultingFromNonTrivialDotDotDotCode;
}
void* Bar::bar(int i)
{
return const_cast<void*>(static_cast<const Bar*>(this)->bar(i));
}
Meyers melanjutkan untuk menjelaskan bahwa memiliki fungsi panggilan const fungsi non-const berbahaya.
Kode di bawah ini adalah contoh tandingan yang menunjukkan:
- bertentangan dengan saran Meyers, kadang-kadang const_cast yang dikombinasikan dengan pemain statis berbahaya
- kadang-kadang memiliki fungsi panggilan const non-const kurang berbahaya
- terkadang kedua cara menggunakan const_cast menyembunyikan kesalahan kompilator yang berguna
- menghindari const_cast dan memiliki anggota pribadi konst tambahan yang mengembalikan non-const adalah pilihan lain
Apakah salah satu dari strategi const_cast menghindari duplikasi kode dianggap praktik yang baik? Apakah Anda lebih suka strategi metode pribadi saja? Apakah ada kasus di mana const_cast akan berfungsi tetapi metode pribadi tidak? Apakah ada opsi lain (selain duplikasi)?
Kekhawatiran saya dengan strategi const_cast adalah bahwa bahkan jika kode itu benar ketika ditulis, nanti selama pemeliharaan kode bisa menjadi salah dan const_cast akan menyembunyikan kesalahan kompiler yang berguna. Sepertinya fungsi pribadi umum umumnya lebih aman.
class Foo
{
public:
Foo(const LongLived& constLongLived, LongLived& mutableLongLived)
: mConstLongLived(constLongLived), mMutableLongLived(mutableLongLived)
{}
// case A: we shouldn't ever be allowed to return a non-const reference to something we only have a const reference to
// const_cast prevents a useful compiler error
const LongLived& GetA1() const { return mConstLongLived; }
LongLived& GetA1()
{
return const_cast<LongLived&>( static_cast<const Foo*>(this)->GetA1() );
}
/* gives useful compiler error
LongLived& GetA2()
{
return mConstLongLived; // error: invalid initialization of reference of type 'LongLived&' from expression of type 'const LongLived'
}
const LongLived& GetA2() const { return const_cast<Foo*>(this)->GetA2(); }
*/
// case B: imagine we are using the convention that const means thread-safe, and we would prefer to re-calculate than lock the cache, then GetB0 might be correct:
int GetB0(int i) { return mCache.Nth(i); }
int GetB0(int i) const { return Fibonachi().Nth(i); }
/* gives useful compiler error
int GetB1(int i) const { return mCache.Nth(i); } // error: passing 'const Fibonachi' as 'this' argument of 'int Fibonachi::Nth(int)' discards qualifiers
int GetB1(int i)
{
return static_cast<const Foo*>(this)->GetB1(i);
}*/
// const_cast prevents a useful compiler error
int GetB2(int i) { return mCache.Nth(i); }
int GetB2(int i) const { return const_cast<Foo*>(this)->GetB2(i); }
// case C: calling a private const member that returns non-const seems like generally the way to go
LongLived& GetC1() { return GetC1Private(); }
const LongLived& GetC1() const { return GetC1Private(); }
private:
LongLived& GetC1Private() const { /* pretend a bunch of lines of code instead of just returning a single variable*/ return mMutableLongLived; }
const LongLived& mConstLongLived;
LongLived& mMutableLongLived;
Fibonachi mCache;
};
class Fibonachi
{
public:
Fibonachi()
{
mCache.push_back(0);
mCache.push_back(1);
}
int Nth(int n)
{
for (int i=mCache.size(); i <= n; ++i)
{
mCache.push_back(mCache[i-1] + mCache[i-2]);
}
return mCache[n];
}
int Nth(int n) const
{
return n < mCache.size() ? mCache[n] : -1;
}
private:
std::vector<int> mCache;
};
class LongLived {};
Jawaban:
Ketika menerapkan fungsi anggota const dan non-const yang hanya berbeda dengan apakah ptr / referensi yang dikembalikan adalah const, strategi KERING terbaik adalah untuk:
misalnya
Mari kita sebut ini fungsi const pribadi yang mengembalikan pola non-const .
Ini adalah strategi terbaik untuk menghindari duplikasi dengan cara yang lurus ke depan sambil tetap memungkinkan kompiler untuk melakukan pemeriksaan yang berpotensi bermanfaat dan melaporkan pesan kesalahan terkait const.
sumber
const
instance (kecuali referensi tersebut untuk sesuatu yang dinyatakanmutable
, atau kecuali Anda menggunakanconst_cast
tetapi dalam kedua kasus tidak ada masalah untuk memulai dengan ). Juga saya tidak bisa menemukan apa pun di "fungsi const pribadi yang mengembalikan pola non-const" (jika itu dimaksudkan untuk menjadi lelucon untuk menyebutnya pola .... itu tidak lucu;)Ya, Anda benar: Banyak program C ++ yang mencoba koreksi benar melanggar prinsip KERING, dan bahkan anggota pribadi yang mengembalikan non-const sedikit terlalu rumit untuk kenyamanan.
Namun, Anda melewatkan satu pengamatan: duplikasi kode karena kebenaran konst hanya pernah menjadi masalah jika Anda memberikan akses kode lain ke anggota data Anda. Ini sendiri merupakan pelanggaran enkapsulasi. Umumnya, duplikasi kode semacam ini kebanyakan terjadi pada pengakses yang sederhana (setelah semua, Anda memberikan akses ke anggota yang sudah ada, nilai pengembalian umumnya bukan hasil perhitungan).
Pengalaman saya adalah bahwa abstraksi yang baik cenderung tidak menyertakan pengakses. Akibatnya, saya sebagian besar menghindari masalah ini dengan mendefinisikan fungsi anggota yang benar-benar melakukan sesuatu, daripada hanya menyediakan akses ke anggota data; Saya mencoba memodelkan perilaku alih-alih data. Tujuan utama saya dalam hal ini adalah untuk benar-benar mendapatkan abstraksi dari kedua kelas saya dan fungsi masing-masing anggota, alih-alih hanya menggunakan objek saya sebagai wadah data. Tetapi gaya ini juga cukup berhasil dalam menghindari berton-ton aksesor satu baris konstan const / non-const yang begitu umum dalam kebanyakan kode.
sumber