Gunakan 'const' untuk parameter fungsi


397

Seberapa jauh Anda melangkah const? Apakah Anda hanya membuat fungsi constsaat diperlukan atau Anda pergi ke seluruh babi dan menggunakannya di mana-mana? Misalnya, bayangkan mutator sederhana yang mengambil parameter boolean tunggal:

void SetValue(const bool b) { my_val_ = b; }

Apakah itu constbenar - benar bermanfaat? Secara pribadi saya memilih untuk menggunakannya secara luas, termasuk parameter, tetapi dalam hal ini saya ingin tahu apakah itu bermanfaat?

Saya juga terkejut mengetahui bahwa Anda dapat menghilangkan constdari parameter dalam deklarasi fungsi tetapi dapat memasukkannya dalam definisi fungsi, misalnya:

file .h

void func(int n, long l);

file .cpp

void func(const int n, const long l)

Apakah ada alasan untuk ini? Sepertinya agak tidak biasa bagi saya.


Saya tidak setuju. File .h juga harus memiliki definisi const. Jika tidak, maka jika parameter const diteruskan ke fungsi, kompiler akan menghasilkan kesalahan, karena prototipe dalam file .h tidak memiliki definisi const.
selwyn

10
Saya setuju. :-) (Dengan pertanyaan, bukan komentar terakhir!) Jika nilai tidak boleh diubah dalam tubuh fungsi, ini dapat membantu menghentikan konyol == atau = bug, Anda tidak boleh meletakkan const di keduanya, (jika itu dilewati oleh nilai, Anda harus sebaliknya) Ini tidak cukup serius untuk masuk ke argumen tentang itu sekalipun!
Chris Huang-Leaver

19
@selwyn: Sekalipun Anda melewatkan konst int ke fungsi, itu akan disalin (karena itu bukan referensi), dan konstanta itu tidak masalah.
jalf

1
Debat yang sama terjadi dalam pertanyaan ini: stackoverflow.com/questions/1554750/…
Partial

5
Saya menyadari posting ini berumur beberapa tahun, tetapi sebagai programmer baru, saya bertanya-tanya pertanyaan ini dan saya menemukan percakapan ini. Menurut pendapat saya, jika suatu fungsi tidak boleh mengubah nilai, apakah itu referensi atau salinan nilai / objek, itu harus berupa const. Lebih aman, mendokumentasikan sendiri, dan lebih ramah debug. Bahkan untuk fungsi paling sederhana, yang memiliki satu pernyataan, saya masih menggunakan const.

Jawaban:


187

Alasannya adalah bahwa const untuk parameter hanya berlaku secara lokal di dalam fungsi, karena ia bekerja pada salinan data. Ini berarti fungsi tanda tangannya benar-benar sama. Ini mungkin gaya yang buruk untuk melakukan ini banyak.

Saya pribadi cenderung tidak menggunakan const kecuali untuk parameter referensi dan pointer. Untuk objek yang disalin tidak masalah, meskipun bisa lebih aman karena memberi sinyal maksud dalam fungsi. Ini benar-benar panggilan penilaian. Saya cenderung menggunakan const_iterator meskipun ketika mengulangi sesuatu dan saya tidak bermaksud memodifikasinya, jadi saya kira masing-masing sendiri, selama kebenaran const untuk tipe referensi dipelihara dengan ketat.


57
Saya tidak bisa setuju dengan bagian 'gaya buruk'. Menjatuhkan constdari prototipe fungsi memiliki keuntungan bahwa Anda tidak perlu mengubah file header jika Anda memutuskan untuk keluar constdari bagian implementasi nanti.
Michał Górny

4
"Saya pribadi cenderung tidak menggunakan const kecuali untuk parameter referensi dan pointer." Mungkin Anda harus mengklarifikasi bahwa untuk "Saya cenderung tidak menggunakan kualifikasi berlebihan dalam deklarasi fungsi, tetapi gunakan di constmana itu membuat perbedaan yang bermanfaat."
Deduplicator

3
Saya tidak setuju dengan jawaban ini. Saya bersandar ke arah lain dan menandai parameter constjika memungkinkan; itu lebih ekspresif. Ketika saya membaca kode orang lain, saya menggunakan indikator kecil seperti ini untuk menilai seberapa besar perhatian mereka dalam menulis kode mereka di samping hal-hal seperti angka ajaib, komentar, dan penggunaan pointer yang tepat, dll.
Ultimater

4
int getDouble(int a){ ++a; return 2*a; }Coba ini. Tentu saja, ++atidak ada yang bisa dilakukan di sana tetapi dapat ditemukan di sana dalam fungsi panjang yang ditulis oleh lebih dari satu programmer selama periode waktu yang lama. Saya akan sangat menyarankan untuk menulis int getDouble( const int a ){ //... }yang akan menghasilkan kesalahan kompilasi ketika menemukan ++a;.
dom_beau

3
Itu semua masalah siapa yang butuh informasi mana. Anda memberikan parameter dengan nilai sehingga penelepon tidak perlu tahu apa pun tentang apa yang Anda lakukan (secara internal) dengannya. Jadi tulis class Foo { int multiply(int a, int b) const; }di tajuk Anda. Dalam pelaksanaannya Anda Anda peduli bahwa Anda dapat berjanji untuk tidak mengubah adan bbegitu int Foo::multiply(const int a, const int b) const { }masuk akal di sini. (Sidenote: baik penelepon dan implementasi peduli tentang fakta bahwa fungsi tidak mengubah Fooobjeknya, dengan demikian konst di akhir deklarasi)
CharonX

415

"const tidak ada gunanya ketika argumen dilewatkan oleh nilai karena Anda tidak akan mengubah objek pemanggil."

Salah.

Ini tentang mendokumentasikan diri sendiri kode Anda dan asumsi Anda.

Jika kode Anda memiliki banyak orang yang mengerjakannya dan fungsi Anda tidak sepele maka Anda harus menandai "const" setiap dan semua yang Anda bisa. Saat menulis kode kekuatan industri, Anda harus selalu berasumsi bahwa rekan kerja Anda adalah psikopat yang berusaha memberi Anda cara apa pun yang mereka bisa (terutama karena sering kali Anda sendiri di masa depan).

Selain itu, sebagai seseorang yang disebutkan sebelumnya, itu mungkin membantu compiler mengoptimalkan hal-hal sedikit (meskipun itu adalah tembakan panjang).


41
Setuju. Ini semua tentang berkomunikasi dengan orang dan membatasi apa yang bisa dilakukan dengan variabel untuk apa yang harus dilakukan.
Len Holgate

19
Saya memilih yang ini. Saya pikir Anda mencairkan apa yang Anda coba tunjukkan dengan const ketika Anda menerapkannya pada argumen nilai sederhana.
tonylo

26
Saya memilih ini. Mendeklarasikan parameter 'const' menambahkan informasi semantik ke parameter. Mereka menyoroti apa yang dimaksudkan oleh pembuat kode asli dan ini akan membantu pemeliharaan kode seiring berjalannya waktu.
Richard Corden

13
@tonylo: Anda salah paham. Ini adalah tentang menandai variabel lokal sebagai const di dalam blok kode (yang kebetulan merupakan fungsi). Saya akan melakukan hal yang sama untuk variabel lokal apa pun. Adalah ortogonal memiliki API yang benar, yang memang juga penting.
rlerallut

56
Dan itu dapat menangkap bug di dalam fungsi - jika Anda tahu bahwa suatu parameter tidak boleh diubah, maka mendeklarasikannya const berarti bahwa kompiler akan memberi tahu Anda jika Anda secara tidak sengaja memodifikasinya.
Adrian

156

Terkadang (terlalu sering!) Saya harus melepaskan kode C ++ orang lain. Dan kita semua tahu bahwa kode C ++ orang lain adalah kekacauan yang lengkap hampir menurut definisi :) Jadi hal pertama yang saya lakukan untuk menguraikan aliran data lokal adalah meletakkan const di setiap definisi variabel sampai kompiler mulai menggonggong. Ini berarti argumen nilai yang memenuhi syarat juga, karena mereka hanya variabel lokal yang bagus yang diinisialisasi oleh pemanggil.

Ah, saya berharap variabel const secara default dan bisa berubah diperlukan untuk variabel non-const :)


4
"Saya berharap variabel adalah const secara default" - sebuah oxymoron ?? 8-) Serius, bagaimana "consting" semuanya membantu Anda mengurai kode? Jika penulis asli mengubah argumen yang seharusnya konstan, bagaimana Anda tahu bahwa var seharusnya konstan? Selain itu, sebagian besar variabel (non-argumen) dimaksudkan sebagai ... variabel. Jadi kompiler akan rusak segera setelah Anda memulai proses, bukan?
ysap

8
@ysap, 1. Menandai const sebanyak mungkin memungkinkan saya melihat bagian mana yang bergerak dan mana yang tidak. Dalam pengalaman saya, banyak penduduk setempat secara de facto const, bukan sebaliknya. 2. "Variabel konstan" / "Variabel tidak dapat diubah" mungkin terdengar sebagai oxymoron, tetapi merupakan praktik standar dalam bahasa fungsional, serta beberapa yang tidak berfungsi; lihat Rust misalnya: doc.rust-lang.org/book/variable-bindings.html
Constantin

1
Juga standar sekarang dalam beberapa keadaan di c ++; misalnya, lambda [x](){return ++x;}adalah kesalahan; lihat di sini
anatolyg

10
Variabelnya " const" secara default di Rust :)
phoenix

Variabel tidak harus merupakan tugas untuk memungkinkan mereka bervariasi; nilai yang diinisialisasi dengan mereka juga dapat bervariasi saat runtime.
Sphynx

80

Dua baris berikut ini setara secara fungsional:

int foo (int a);
int foo (const int a);

Jelas Anda tidak akan dapat memodifikasi adalam tubuh foojika itu didefinisikan dengan cara kedua, tetapi tidak ada perbedaan dari luar.

Yang constsangat berguna adalah dengan parameter referensi atau penunjuk:

int foo (const BigStruct &a);
int foo (const BigStruct *a);

Apa yang dikatakan ini adalah bahwa foo dapat mengambil parameter besar, mungkin struktur data yang berukuran gigabytes, tanpa menyalinnya. Juga, ia berkata kepada penelepon, "Foo tidak akan * mengubah isi parameter itu." Melewati referensi const juga memungkinkan kompiler untuk membuat keputusan kinerja tertentu.

*: Kecuali jika itu menghilangkan konstanta, tetapi itu adalah pos lainnya.


3
Bukan itu pertanyaannya; tentu saja untuk argumen yang direferensikan atau menunjuk-ke ​​itu adalah ide yang baik untuk menggunakan const (jika nilai yang direferensikan atau menunjuk-ke ​​tidak dimodifikasi). Perhatikan bahwa itu bukan parameter yang const dalam contoh pointer Anda; itu adalah hal yang ditunjukkan oleh parameter.
tml

> Melewati referensi const juga memungkinkan kompiler membuat keputusan kinerja tertentu. fallacy klasik - kompiler harus menentukan sendiri konstanta, kata kunci const tidak membantu dengan itu berkat pointer aliasing dan const_cast
jheriko

73

Const Superfluous Ekstra buruk dari sudut pandang API:

Menempatkan const berlebihan di kode Anda untuk parameter tipe intrinsik yang diteruskan oleh nilai mengacaukan API Anda sementara tidak membuat janji yang berarti bagi pemanggil atau pengguna API (itu hanya menghambat implementasi).

Terlalu banyak 'const' dalam API ketika tidak diperlukan seperti " menangis serigala ", akhirnya orang akan mulai mengabaikan 'const' karena semuanya ada di mana-mana dan hampir tidak berarti apa-apa.

Argumen "reductio ad absurdum" untuk const tambahan di API baik untuk dua poin pertama ini adalah jika parameter const lebih baik, maka setiap argumen yang dapat memiliki const di atasnya, HARUS memiliki const di atasnya. Bahkan, jika itu benar-benar baik, Anda ingin const menjadi default untuk parameter dan memiliki kata kunci seperti "bisa berubah" hanya ketika Anda ingin mengubah parameter.

Jadi mari kita coba letakkan const di mana pun kita bisa:

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);

Pertimbangkan baris kode di atas. Tidak hanya deklarasi yang lebih berantakan dan lebih lama dan lebih sulit untuk dibaca tetapi tiga dari empat kata kunci 'const' dapat diabaikan dengan aman oleh pengguna API. Namun, penggunaan ekstra 'const' telah membuat baris kedua berpotensi BERBAHAYA!

Mengapa?

Kesalahan membaca cepat dari parameter pertama char * const buffermungkin membuat Anda berpikir bahwa itu tidak akan mengubah memori dalam buffer data yang diteruskan - namun, ini tidak benar! 'Const' berlebihan dapat menyebabkan asumsi berbahaya dan tidak benar tentang API Anda ketika dipindai atau salah dibaca dengan cepat.


Konstanta yang berlebih buruk dari sudut pandang Implementasi Kode juga:

#if FLEXIBLE_IMPLEMENTATION
       #define SUPERFLUOUS_CONST
#else
       #define SUPERFLUOUS_CONST             const
#endif

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count);

Jika FLEXIBLE_IMPLEMENTATION tidak benar, maka API "menjanjikan" untuk tidak mengimplementasikan fungsi dengan cara pertama di bawah ini.

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       // Will break if !FLEXIBLE_IMPLEMENTATION
       while(count--)
       {
              *dest++=*source++;
       }
}

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       for(int i=0;i<count;i++)
       {
              dest[i]=source[i];
       }
}

Itu janji yang sangat konyol untuk dibuat. Mengapa Anda harus membuat janji yang sama sekali tidak bermanfaat bagi penelepon Anda dan hanya membatasi implementasi Anda?

Kedua hal ini adalah implementasi yang benar-benar valid dari fungsi yang sama, jadi semua yang Anda lakukan adalah terikat satu tangan di belakang Anda tanpa perlu.

Selain itu, itu adalah janji yang sangat dangkal yang mudah (dan secara hukum dielakkan).

inline void bytecopyWrapped(char * dest,
   const char *source, int count)
{
       while(count--)
       {
              *dest++=*source++;
       }
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source,SUPERFLUOUS_CONST int count)
{
    bytecopyWrapped(dest, source, count);
}

Lihat, saya menerapkannya bagaimanapun saya berjanji untuk tidak - hanya menggunakan fungsi wrapper. Ini seperti ketika penjahat itu berjanji untuk tidak membunuh seseorang dalam film dan memerintahkan anteknya untuk membunuh mereka.

Konstanta yang berlebihan itu tidak lebih dari janji dari penjahat film.


Tetapi kemampuan untuk berbohong semakin buruk:

Saya telah tercerahkan bahwa Anda dapat ketidakcocokan const di header (deklarasi) dan kode (definisi) dengan menggunakan const palsu. Para pendukung const-happy mengklaim ini adalah hal yang baik karena memungkinkan Anda untuk menempatkan const hanya dalam definisi.

// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }

Namun, kebalikannya benar ... Anda dapat menempatkan const palsu hanya dalam deklarasi dan mengabaikannya dalam definisi. Ini hanya membuat const berlebihan di API lebih dari hal yang mengerikan dan kebohongan yang mengerikan - lihat contoh ini:

class foo
{
    void test(int * const pi);
};

void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
    pi++;  // I promised in my definition I wouldn't modify this
}

Semua const yang berlebihan sebenarnya dilakukan adalah membuat kode implementer kurang mudah dibaca dengan memaksanya untuk menggunakan copy lokal atau fungsi wrapper ketika dia ingin mengubah variabel atau meneruskan variabel dengan referensi non-const.

Lihatlah contoh ini. Mana yang lebih mudah dibaca? Apakah jelas bahwa satu-satunya alasan untuk variabel tambahan dalam fungsi kedua adalah karena beberapa perancang API melemparkan konstanta yang berlebihan?

struct llist
{
    llist * next;
};

void walkllist(llist *plist)
{
    llist *pnext;
    while(plist)
    {
        pnext=plist->next;
        walk(plist);
        plist=pnext;    // This line wouldn't compile if plist was const
    }
}

void walkllist(llist * SUPERFLUOUS_CONST plist)
{
    llist * pnotconst=plist;
    llist *pnext;
    while(pnotconst)
    {
        pnext=pnotconst->next;
        walk(pnotconst);
        pnotconst=pnext;
    }
}

Semoga kita sudah belajar sesuatu di sini. Superfluous const adalah merusak pemandangan API, mengomel menjengkelkan, janji dangkal dan tidak berarti, hambatan yang tidak perlu, dan kadang-kadang menyebabkan kesalahan yang sangat berbahaya.


9
Mengapa downvotes? Jauh lebih bermanfaat jika Anda memberikan komentar singkat pada downvote.
Adisak

7
Inti dari menggunakan argumen const adalah untuk membuat garis yang ditandai gagal (plist = pnext). Ini adalah tindakan keamanan yang wajar untuk menjaga argumen fungsi tidak berubah. Saya setuju dengan poin Anda bahwa mereka buruk dalam deklarasi fungsi (karena mereka berlebihan), tetapi mereka dapat melayani tujuan mereka di blok implementasi.
touko

23
@Adisak Saya tidak melihat ada yang salah dengan jawaban Anda, tapi, sepertinya dari komentar Anda bahwa Anda kehilangan poin penting. Definisi / implementasi fungsi bukan bagian dari API, yang hanya merupakan deklarasi fungsi . Seperti yang Anda katakan, mendeklarasikan fungsi dengan parameter const tidak ada gunanya dan menambah kekacauan. Namun pengguna API mungkin tidak perlu melihat implementasinya. Sementara itu pelaksana dapat memutuskan untuk membuat kualifikasi beberapa parameter dalam definisi fungsi HANYA untuk kejelasan, yang sangat bagus.
jw013

17
@ jw013 sudah benar, void foo(int)dan void foo(const int)fungsinya sama persis, bukan kelebihan beban. ideone.com/npN4W4 ideone.com/tZav9R Const di sini hanya merupakan detail implementasi dari fungsi tubuh, dan tidak berpengaruh pada resolusi kelebihan beban. Biarkan const keluar dari deklarasi, untuk API yang lebih aman dan rapi, tetapi masukkan const ke dalam definisi , jika Anda tidak ingin mengubah nilai yang disalin.
Oktalist

3
@Adisak Saya tahu ini sudah tua, tapi saya percaya penggunaan yang benar untuk API publik akan sebaliknya. Dengan begitu pengembang yang bekerja di internal tidak membuat kesalahan seperti pi++ketika mereka tidak seharusnya melakukannya.
CoffeeandCode

39

const seharusnya menjadi default di C ++. Seperti ini :

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable

8
Pikiranku persis.
Constantin

24
Kompatibilitas dengan C terlalu penting, setidaknya bagi orang yang mendesain C ++, untuk mempertimbangkan hal ini.

4
Menarik, saya tidak pernah memikirkan itu.
Dan

6
Demikian pula, unsignedseharusnya menjadi default di C ++. Seperti ini: int i = 5; // i is unsigneddan signed int i = 5; // i is signed.
hkBattousai

25

Ketika saya mengkodekan C ++ sebagai mata pencaharian saya mengumpulkan semua yang saya bisa. Menggunakan const adalah cara yang bagus untuk membantu kompiler membantu Anda. Misalnya, dengan mencantumkan nilai pengembalian metode Anda dapat menyelamatkan Anda dari kesalahan ketik seperti:

foo() = 42

ketika Anda maksud:

foo() == 42

Jika foo () didefinisikan untuk mengembalikan referensi non-const:

int& foo() { /* ... */ }

Kompiler akan dengan senang hati membiarkan Anda memberikan nilai kepada anonim sementara yang dikembalikan oleh panggilan fungsi. Membuatnya menjadi:

const int& foo() { /* ... */ }

Hilangkan kemungkinan ini.


6
Dengan kompiler apa itu bekerja? GCC memberikan kesalahan saat mencoba mengompilasi foo() = 42: error: lvalue diperlukan sebagai operan penugasan yang
tersisa

Ini tidak benar. foo () = 42 sama dengan 2 = 3, yaitu kesalahan kompiler. Dan mengembalikan const sama sekali tidak ada gunanya. Itu tidak melakukan apa pun untuk tipe bawaan.
Josh

2
Saya telah menemukan penggunaan const ini dan saya dapat memberi tahu Anda, pada akhirnya menghasilkan lebih banyak kerumitan daripada manfaat. Petunjuk: const int foo()adalah tipe yang berbeda dari int foo(), yang membawa Anda ke masalah besar jika Anda menggunakan hal-hal seperti pointer fungsi, sistem sinyal / slot atau boost :: bind.
Mephane

2
Saya telah memperbaiki kode untuk memasukkan nilai pengembalian referensi.
Avdi

Bukankah const int& foo()secara efektif sama dengan int foo(), karena pengembalian nilai optimasi?
Zantier

15

Ada diskusi yang bagus tentang topik ini di artikel "Guru Minggu Ini" yang lama di comp.lang.c ++. Dimoderasi di sini .

Artikel GOTW yang sesuai tersedia di situs web Herb Sutter di sini .


1
Herb Sutter adalah orang yang sangat pintar :-) Pasti layak dibaca dan saya setuju dengan SEMUA poinnya.
Adisak

2
Artikel bagus tapi saya tidak setuju dengannya tentang argumen. Saya membuat mereka juga const karena mereka seperti variabel, dan saya tidak pernah ingin ada yang membuat perubahan pada argumen saya.
QBziZ

9

Saya katakan const parameter nilai Anda.

Pertimbangkan fungsi buggy ini:

bool isZero(int number)
{
  if (number = 0)  // whoops, should be number == 0
    return true;
  else
    return false;
}

Jika parameter angka adalah const, kompiler akan berhenti dan memperingatkan kami tentang bug.


2
cara lain menggunakan if (0 == number) ... else ...;
Johannes Schaub - litb

5
@ ChrisHuang-Leaver Mengerikan itu tidak, jika berbicara seperti Yoda Anda lakukan: stackoverflow.com/a/2430307/210916
MPelletier

GCC / Dentang -Dinding memberi Anda -Dapur, yang menuntut Anda membuatnya "jika ((angka = 0))" jika memang itulah yang ingin Anda lakukan. Yang berfungsi baik sebagai pengganti Yoda.
Jetski S-ketik

8

Saya menggunakan const pada parameter fungsi yang merupakan referensi (atau pointer) yang hanya data [dalam] dan tidak akan dimodifikasi oleh fungsi. Artinya, ketika tujuan menggunakan referensi adalah untuk menghindari menyalin data dan tidak mengizinkan mengubah parameter yang dikirimkan.

Menempatkan const pada parameter boolean b dalam contoh Anda hanya memberikan kendala pada implementasi dan tidak berkontribusi untuk antarmuka kelas (meskipun biasanya tidak mengubah parameter).

Tanda tangan fungsi untuk

void foo(int a);

dan

void foo(const int a);

sama, yang menjelaskan .c dan .h Anda

Asaf


6

Jika Anda menggunakan operator ->*atau .*, itu suatu keharusan.

Ini mencegah Anda menulis sesuatu seperti

void foo(Bar *p) { if (++p->*member > 0) { ... } }

yang hampir saya lakukan sekarang, dan yang mungkin tidak melakukan apa yang Anda inginkan.

Yang ingin saya katakan adalah

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }

dan jika saya meletakkan constdi antara Bar *dan p, kompiler akan memberi tahu saya itu.


4
Saya akan segera memeriksa referensi tentang prioritas operator ketika saya akan menggabungkan banyak operator (jika saya belum tahu 100%), jadi IMO itu bukan masalah.
mk12

5

Ah, yang sulit. Di satu sisi, deklarasi adalah kontrak dan itu benar-benar tidak masuk akal untuk melewati argumen const dengan nilai. Di sisi lain, jika Anda melihat implementasi fungsi, Anda memberi kompiler lebih banyak kesempatan untuk mengoptimalkan jika Anda menyatakan argumen konstan.


5

const tidak ada gunanya ketika argumen dilewatkan oleh nilai karena Anda tidak akan mengubah objek pemanggil.

const harus lebih disukai ketika melewati referensi, kecuali tujuan fungsi adalah untuk mengubah nilai yang dikirimkan.

Akhirnya, fungsi yang tidak mengubah objek saat ini (ini) dapat, dan mungkin harus dinyatakan sebagai const. Contohnya di bawah ini:

int SomeClass::GetValue() const {return m_internalValue;}

Ini adalah janji untuk tidak mengubah objek yang digunakan untuk panggilan ini. Dengan kata lain, Anda dapat menelepon:

const SomeClass* pSomeClass;
pSomeClass->GetValue();

Jika fungsinya bukan const, ini akan menghasilkan peringatan compiler.


5

Menandai parameter nilai 'const' jelas merupakan hal yang subyektif.

Namun saya sebenarnya lebih suka menandai parameter nilai const, seperti pada contoh Anda.

void func(const int n, const long l) { /* ... */ }

Nilai bagi saya dengan jelas menunjukkan bahwa nilai parameter fungsi tidak pernah diubah oleh fungsi. Mereka akan memiliki nilai yang sama di awal dan di akhir. Bagi saya, ini adalah bagian dari menjaga gaya pemrograman yang sangat fungsional.

Untuk fungsi pendek, bisa dibilang buang waktu / ruang untuk memiliki 'const' di sana, karena biasanya cukup jelas bahwa argumen tidak dimodifikasi oleh fungsi.

Namun untuk fungsi yang lebih besar, ini merupakan bentuk dokumentasi implementasi, dan ini diberlakukan oleh kompiler.

Saya dapat yakin jika saya membuat perhitungan dengan 'n' dan 'l', saya dapat menolak / memindahkan perhitungan itu tanpa takut mendapatkan hasil yang berbeda karena saya melewatkan tempat di mana satu atau keduanya diubah.

Karena ini adalah detail implementasi, Anda tidak perlu mendeklarasikan parameter nilai const di header, sama seperti Anda tidak perlu mendeklarasikan parameter fungsi dengan nama yang sama dengan implementasi yang digunakan.


4

Mungkin ini tidak akan menjadi argumen yang valid. tetapi jika kita menambah nilai variabel const di dalam kompiler fungsi akan memberi kita kesalahan: " error: increment of read-only parameter ". jadi itu berarti kita dapat menggunakan kata kunci const sebagai cara untuk mencegah secara tidak sengaja memodifikasi variabel kita di dalam fungsi (yang seharusnya tidak kita baca / baca-saja). jadi jika kita tidak sengaja melakukannya pada waktu kompilasi, kompiler akan memberi tahu kami. ini sangat penting jika Anda bukan satu-satunya yang mengerjakan proyek ini.


3

Saya cenderung menggunakan const sedapat mungkin. (Atau kata kunci lain yang sesuai untuk bahasa target.) Saya melakukan ini murni karena memungkinkan kompiler membuat optimisasi tambahan yang tidak dapat dibuat sebaliknya. Karena saya tidak tahu apa optimasi ini, saya selalu melakukannya, bahkan ketika itu tampak konyol.

Yang saya tahu, kompiler mungkin sangat baik melihat parameter nilai const, dan berkata, "Hei, fungsi ini tidak memodifikasinya, jadi saya bisa melewati referensi dan menyimpan beberapa siklus jam." Saya tidak berpikir itu akan melakukan hal seperti itu, karena itu mengubah tanda tangan fungsi, tetapi itu yang penting. Mungkin itu melakukan beberapa manipulasi tumpukan yang berbeda atau sesuatu ... Intinya adalah, saya tidak tahu, tapi saya tahu mencoba menjadi lebih pintar daripada kompiler hanya membuat saya dipermalukan.

C ++ memiliki beberapa bagasi tambahan, dengan gagasan const-correctness, sehingga menjadi lebih penting.


Sementara itu mungkin membantu dalam beberapa kasus, saya menduga kemungkinan mempromosikan optimisasi secara berlebihan dilebih-lebihkan sebagai manfaatnya const. Sebaliknya, itu adalah masalah menyatakan niat dalam implementasi dan menangkap thinkoes nanti (secara tidak sengaja menambah variabel lokal yang salah, karena tidak const). Secara paralel, saya juga menambahkan bahwa kompiler dipersilakan untuk mengubah tanda tangan fungsi, dalam arti bahwa fungsi dapat digariskan, dan sekali digariskan seluruh cara kerjanya dapat diubah; menambah atau menghapus referensi, membuat literal 'variabel', dll semuanya berada dalam aturan as-if
underscore_d

3

1. Jawaban terbaik berdasarkan penilaian saya:

Jawaban oleh @Adisak adalah jawaban terbaik di sini berdasarkan penilaian saya. Perhatikan bahwa jawaban ini sebagian adalah yang terbaik karena ini juga merupakan yang paling didukung dengan contoh kode nyata , selain menggunakan logika suara dan pemikiran yang matang.

2. Kata-kata saya sendiri (setuju dengan jawaban terbaik):

  1. Untuk pass-by-value tidak ada manfaatnya menambahkan const. Yang dilakukannya hanyalah:
    1. batasi implementer untuk harus membuat salinan setiap kali mereka ingin mengubah parameter input dalam kode sumber (yang perubahannya tidak akan memiliki efek samping karena apa yang diteruskan sudah merupakan salinan karena sudah melewati nilai). Dan sering, mengubah param input yang dilewatkan oleh nilai digunakan untuk mengimplementasikan fungsi, sehingga menambahkan di constmana-mana dapat menghambat ini.
    2. dan menambahkan yang consttidak perlu mengacaukan kode dengan constdi mana-mana, menarik perhatian dari constyang benar-benar diperlukan untuk memiliki kode aman.
  2. Ketika berhadapan dengan pointer atau referensi , bagaimanapun, constsangat penting ketika dibutuhkan, dan harus digunakan, karena mencegah efek samping yang tidak diinginkan dengan perubahan terus-menerus di luar fungsi, dan oleh karena itu setiap pointer atau referensi tunggal harus menggunakanconst ketika parm adalah input saja, bukan output. Menggunakan const hanya pada parameter yang dilewatkan oleh referensi atau pointer memiliki manfaat tambahan membuatnya sangat jelas parameter mana yang pointer atau referensi. Ada satu hal lagi yang perlu diperhatikan dan katakan, "Awas! Setiap param dengan di constsebelahnya adalah referensi atau pointer!".
  3. Apa yang saya jelaskan di atas sering kali merupakan konsensus yang dicapai dalam organisasi perangkat lunak profesional tempat saya bekerja, dan telah dianggap sebagai praktik terbaik. Kadang-kadang bahkan, aturannya sangat ketat: "jangan pernah menggunakan const pada parameter yang dilewati oleh nilai, tetapi selalu gunakan itu pada parameter yang dilewatkan oleh referensi atau pointer jika hanya input."

3. Kata-kata Google (setuju dengan saya dan jawaban terbaik):

(Dari " Panduan Gaya Google C ++ ")

Untuk parameter fungsi yang diteruskan oleh nilai, const tidak berpengaruh pada pemanggil, sehingga tidak direkomendasikan dalam deklarasi fungsi. Lihat TotW # 109 .

Menggunakan const pada variabel lokal tidak dianjurkan atau tidak dianjurkan.

Sumber: bagian "Penggunaan konst" pada Panduan Gaya Google C ++: https://google.github.io/styleguide/cppguide.html#Use_of_const . Ini sebenarnya bagian yang sangat berharga, jadi bacalah seluruh bagian ini.

Perhatikan bahwa "TotW # 109" adalah singkatan dari "Tip of the Week # 109: Berarti constdalam Deklarasi Fungsi" , dan juga merupakan bacaan yang bermanfaat. Ini lebih informatif dan kurang preskriptif tentang apa yang harus dilakukan, dan berdasarkan konteks muncul sebelum aturan Panduan Gaya Google C ++ constdikutip di atas, tetapi sebagai hasil dari kejelasan yang diberikan, constaturan yang dikutip di atas ditambahkan ke Google C ++ Panduan Gaya.

Perhatikan juga bahwa meskipun saya mengutip Panduan Gaya Google C ++ di sini untuk mempertahankan posisi saya, itu TIDAK berarti saya selalu mengikuti panduan ini atau selalu merekomendasikan untuk mengikuti panduan ini. Beberapa hal yang mereka sarankan adalah sekadar aneh, seperti mereka kDaysInAWeekkonvensi penamaan -gaya untuk "Nama Constant" . Namun, tetap berguna dan relevan untuk ditunjukkan ketika salah satu perusahaan teknis dan perangkat lunak paling sukses dan berpengaruh di dunia menggunakan justifikasi yang sama seperti yang saya dan orang lain seperti @Adisak lakukan untuk mendukung sudut pandang kami tentang masalah ini.

4. Linter clang clang-tidy,, memiliki beberapa opsi untuk ini:

A. Perlu juga dicatat bahwa linter Clang clang-tidy,, memiliki opsi readability-avoid-const-params-in-decls,, yang dijelaskan di sini , untuk mendukung penegakan dalam basis kode yang tidak menggunakan constparameter fungsi pass-by-value :

Cek apakah deklarasi fungsi memiliki parameter yang merupakan konstanta tingkat atas.

Nilai-nilai const dalam deklarasi tidak mempengaruhi tanda tangan suatu fungsi, jadi mereka tidak boleh diletakkan di sana.

Contoh:

void f(const string);   // Bad: const is top level.
void f(const string&);  // Good: const is not top level.

Dan berikut adalah dua contoh lagi yang saya tambahkan untuk kelengkapan dan kejelasan:

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]

B. Ini juga memiliki opsi ini: readability-const-return-type- https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

5. Pendekatan pragmatis saya tentang bagaimana saya akan mengatakan panduan gaya tentang masalah ini:

Saya cukup menyalin dan menempelkan ini ke panduan gaya saya:

[SALIN / PASTE MULAI]

  1. Selalu gunakan const pada parameter fungsi yang dilewatkan oleh referensi atau pointer ketika isinya (apa yang mereka tunjukkan) dimaksudkan TIDAK untuk diubah. Dengan cara ini, menjadi jelas ketika variabel yang dilewatkan oleh referensi atau pointer IS diharapkan akan berubah, karena itu akan kurang const. Dalam hal ini digunakanconst mencegah efek samping yang tidak disengaja di luar fungsi.
  2. Tidak disarankan untuk menggunakan constparameter fungsi yang diteruskan oleh nilai, karenaconst tidak memiliki efek pada pemanggil: bahkan jika variabel diubah dalam fungsi tidak akan ada efek samping di luar fungsi. Lihat sumber daya berikut untuk justifikasi dan wawasan tambahan:
    1. "Panduan Gaya Google C ++" "Penggunaan bagian const"
    2. "Tip of the Week # 109: Makna constdalam Deklarasi Fungsi"
    3. Jawaban Stack Overflow Adisak pada "Penggunaan 'const' untuk parameter fungsi"
  3. " Jangan pernah menggunakan level atas const[yaitu: constpada parameter yang diteruskan oleh nilai ] pada parameter fungsi dalam deklarasi yang bukan definisi (dan hati-hati untuk tidak menyalin / menempelkan yang tidak berarti const). Itu tidak ada artinya dan diabaikan oleh kompiler, itu adalah gangguan visual , dan itu dapat menyesatkan pembaca "( https://abseil.io/tips/109 , penekanan ditambahkan).
    1. Satu-satunya constkualifikasi yang memiliki efek pada kompilasi adalah yang ditempatkan dalam definisi fungsi, BUKAN mereka dalam deklarasi maju dari fungsi, seperti dalam deklarasi fungsi (metode) dalam file header.
  4. Jangan pernah menggunakan level atas const[yaitu: constpada variabel yang dikirimkan oleh nilai ] pada nilai yang dikembalikan oleh suatu fungsi.
  5. Menggunakan constpointer atau referensi yang dikembalikan oleh suatu fungsi tergantung pada implementer , karena terkadang berguna.
  6. TODO: tegakkan beberapa hal di atas dengan clang-tidyopsi berikut :
    1. https://clang.llvm.org/extra/clang-tidy/checks/readability-avoid-const-params-in-decls.html
    2. https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

Berikut adalah beberapa contoh kode untuk menunjukkan constaturan yang dijelaskan di atas:

constContoh Parameter:
(ada yang dipinjam dari sini )

void f(const std::string);   // Bad: const is top level.
void f(const std::string&);  // Good: const is not top level.

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]

constContoh Jenis Pengembalian:
(beberapa dipinjam dari sini )

// BAD--do not do this:
const int foo();
const Clazz foo();
Clazz *const foo();

// OK--up to the implementer:
const int* foo();
const int& foo();
const Clazz* foo();

[SALIN / PASTE END]


2

Dalam kasus yang Anda sebutkan, itu tidak memengaruhi penelepon API Anda, itulah sebabnya itu tidak biasa dilakukan (dan tidak perlu di header). Itu hanya mempengaruhi implementasi fungsi Anda.

Ini tidak terlalu buruk untuk dilakukan, tetapi manfaatnya tidak terlalu besar mengingat itu tidak memengaruhi API Anda, dan itu menambah pengetikan, jadi itu biasanya tidak dilakukan.


2

Saya tidak menggunakan const untuk parametere nilai-lulus. Penelepon tidak peduli apakah Anda memodifikasi parameter atau tidak, ini merupakan detail implementasi.

Yang benar-benar penting adalah menandai metode sebagai const jika mereka tidak mengubah instansinya. Lakukan ini sambil berjalan, karena jika tidak, Anda mungkin berakhir dengan banyak const_cast <> atau Anda mungkin menemukan bahwa menandai metode const memerlukan perubahan banyak kode karena memanggil metode lain yang seharusnya ditandai dengan const.

Saya juga cenderung menandai const vars lokal jika saya tidak perlu memodifikasinya. Saya percaya itu membuat kode lebih mudah dimengerti dengan membuatnya lebih mudah untuk mengidentifikasi "bagian yang bergerak".



2

Saya menggunakan const jika saya bisa. Konstan untuk parameter berarti bahwa mereka tidak boleh mengubah nilainya. Ini sangat berharga ketika melewati referensi. const untuk fungsi menyatakan bahwa fungsi tidak boleh mengubah anggota kelas.


2

Untuk meringkas:

  • "Biasanya nilai-by-const tidak berguna dan menyesatkan." Dari GOTW006
  • Tapi Anda bisa menambahkannya di .cpp seperti yang Anda lakukan dengan variabel.
  • Perhatikan bahwa perpustakaan standar tidak menggunakan const. Misalnya std::vector::at(size_type pos). Apa yang cukup baik untuk perpustakaan standar baik untuk saya.

2
"Apa yang cukup baik untuk perpustakaan standar baik untuk saya" tidak selalu benar. Misalnya, pustaka standar menggunakan nama variabel jelek seperti _Tmpsepanjang waktu - Anda tidak menginginkan ini (sebenarnya Anda tidak diizinkan menggunakannya).
anatolyg

1
@anatolyg ini adalah detail implementasi
Fernando Pelliccioni

2
OK, kedua nama variabel dan tipe const-kualifikasi dalam daftar argumen adalah detail implementasi Yang ingin saya katakan adalah, implementasi perpustakaan standar terkadang tidak baik. Terkadang, Anda dapat (dan harus) melakukan lebih baik. Kapan kode untuk perpustakaan standar ditulis - 10 tahun yang lalu? 5 tahun yang lalu (beberapa bagian terbaru)? Kita dapat menulis kode yang lebih baik hari ini.
anatolyg

1

Jika parameter dilewatkan oleh nilai (dan bukan referensi), biasanya tidak ada banyak perbedaan apakah parameter dinyatakan sebagai const atau tidak (kecuali jika berisi anggota referensi - bukan masalah untuk tipe bawaan). Jika parameternya adalah referensi atau pointer, biasanya lebih baik untuk melindungi memori yang direferensikan / menunjuk-ke, bukan pointer itu sendiri (saya pikir Anda tidak dapat membuat referensi itu sendiri const, bukan yang penting karena Anda tidak dapat mengubah wasit) . Tampaknya ide yang baik untuk melindungi semua yang Anda bisa sebagai const. Anda dapat menghilangkannya tanpa takut membuat kesalahan jika parameternya hanya POD (termasuk tipe bawaan) dan tidak ada kemungkinan mereka berubah lebih jauh di sepanjang jalan (misalnya dalam contoh Anda parameter bool).

Saya tidak tahu tentang perbedaan deklarasi file .h / .cpp, tapi itu masuk akal. Pada level kode mesin, tidak ada yang "const", jadi jika Anda mendeklarasikan fungsi (dalam .h) sebagai non-const, kodenya sama seperti jika Anda mendeklarasikannya sebagai const (selain optimasi). Namun, ini membantu Anda untuk meminta kompiler bahwa Anda tidak akan mengubah nilai variabel di dalam implementasi fungsi (.ccp). Mungkin berguna jika Anda mewarisi dari antarmuka yang memungkinkan perubahan, tetapi Anda tidak perlu mengubah ke parameter untuk mencapai fungsionalitas yang diperlukan.


0

Saya tidak akan menaruh const pada parameter seperti itu - semua orang sudah tahu bahwa boolean (bukan boolean &) adalah konstan, jadi menambahkannya akan membuat orang berpikir "tunggu, apa?" atau bahkan Anda melewati parameter dengan referensi.


4
kadang-kadang Anda ingin melewatkan objek dengan referensi (untuk alasan kinerja) tetapi tidak mengubahnya, jadi const wajib saat itu. Menjaga semua parameter seperti itu - bahkan bools - const akan menjadi praktik yang baik, membuat kode Anda lebih mudah dibaca.
gbjbaanb

0

hal yang perlu diingat dengan const adalah bahwa jauh lebih mudah untuk membuat hal-hal const sejak awal, daripada mencoba dan memasukkannya nanti.

Gunakan const ketika Anda menginginkan sesuatu tidak berubah - ini adalah petunjuk tambahan yang menjelaskan apa fungsi Anda dan apa yang diharapkan. Saya telah melihat banyak C API yang dapat dilakukan dengan beberapa dari mereka, terutama yang menerima c-string!

Saya akan lebih cenderung menghilangkan kata kunci const di file cpp daripada header, tetapi karena saya cenderung untuk memotong + menempelkannya, mereka akan disimpan di kedua tempat. Saya tidak tahu mengapa kompiler memungkinkan, saya kira ini adalah kompiler. Praktik terbaik pasti menempatkan kata kunci const Anda di kedua file.


Saya tidak mendapatkan ini sama sekali. Mengapa Anda cenderung menghilangkannya di file cpp (definisi fungsi)? Di situlah arti sebenarnya sesuatu dan dapat menangkap kesalahan. Mengapa Anda pikir itu praktek terbaik untuk menaruh const di kedua tempat? Dalam file header (deklarasi fungsi), tidak ada artinya dan mengacaukan API. Mungkin ada beberapa nilai kecil untuk memiliki mendeklarasikan dan defn terlihat persis sama, tetapi menurut saya itu manfaat yang sangat kecil dibandingkan dengan masalah mengacaukan API.
Don Hatch

@ DonHatch 8 tahun kemudian, wow. Lagi pula, seperti kata OP "Saya juga terkejut mengetahui bahwa Anda dapat menghilangkan const dari parameter dalam deklarasi fungsi tetapi dapat memasukkannya dalam definisi fungsi".
gbjbaanb

0

Sebenarnya tidak ada alasan untuk membuat nilai-parameter "const" karena fungsinya hanya dapat memodifikasi salinan variabel.

Alasan untuk menggunakan "const" adalah jika Anda melewatkan sesuatu yang lebih besar (misalnya struct dengan banyak anggota) dengan referensi, dalam hal ini memastikan bahwa fungsi tidak dapat memodifikasinya; atau lebih tepatnya, kompiler akan mengeluh jika Anda mencoba memodifikasinya dengan cara konvensional. Ini mencegahnya agar tidak sengaja dimodifikasi.


0

Parameter const hanya berguna ketika parameter dilewatkan oleh referensi yaitu, baik referensi atau pointer. Ketika kompiler melihat parameter const, itu memastikan bahwa variabel yang digunakan dalam parameter tidak dimodifikasi di dalam tubuh fungsi. Mengapa ada orang yang ingin membuat parameter nilai-sebagai konstan? :-)


Untuk banyak alasan. Membuat parameter menurut nilai constmenyatakan dengan jelas: 'Saya tidak perlu memodifikasi ini, jadi saya menyatakan itu. Jika saya mencoba memodifikasinya nanti, beri saya kesalahan waktu kompilasi sehingga saya dapat memperbaiki kesalahan saya atau tidak menandai sebagai const. ' Jadi ini masalah kebersihan dan keamanan kode. Untuk semua yang diperlukan untuk menambahkan file implementasi, itu harus menjadi sesuatu yang orang lakukan sebagai refleks murni, IMO.
underscore_d

0

Sebagai parameter yang diteruskan oleh nilai, tidak ada bedanya jika Anda menentukan const atau tidak dari perspektif fungsi pemanggilan. Pada dasarnya tidak masuk akal untuk mendeklarasikan pass by parameter nilai sebagai const.


0

Semua const dalam contoh Anda tidak memiliki tujuan. C ++ adalah pass-by-value secara default, sehingga fungsinya mendapatkan salinan int dan boolean tersebut. Bahkan jika fungsi tidak memodifikasinya, salinan pemanggil tidak terpengaruh.

Jadi saya akan menghindari tambahan const karena

  • Mereka redudant
  • Mereka mengacaukan teks
  • Mereka mencegah saya dari mengubah nilai yang diteruskan dalam kasus di mana itu mungkin berguna atau efisien.

-1

Saya tahu pertanyaannya "sedikit" ketinggalan jaman, tetapi ketika saya menemukan itu orang lain mungkin juga melakukannya di masa depan ... ... masih saya ragu orang miskin akan daftar di sini untuk membaca komentar saya :)

Sepertinya saya bahwa kita masih terlalu terbatas pada cara berpikir gaya-C. Dalam paradigma OOP kita bermain-main dengan objek, bukan tipe. Objek Const dapat secara konseptual berbeda dari objek non-const, khususnya dalam arti logical-const (berbeda dengan bitwise-const). Jadi, bahkan jika kebenaran konstanta fungsi params (mungkin) terlalu berhati-hati dalam hal POD, tidak demikian halnya pada objek. Jika suatu fungsi bekerja dengan objek const, seharusnya dikatakan demikian. Pertimbangkan potongan kode berikut

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

ps .: Anda mungkin berpendapat bahwa referensi (const) akan lebih sesuai di sini dan memberi Anda perilaku yang sama. Ya benar. Hanya memberikan gambar yang berbeda dari apa yang bisa saya lihat di tempat lain ...

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.