Header mana yang harus saya sertakan untuk `size_t`?

98

Menurut cppreference.com size_t didefinisikan dalam beberapa header, yaitu

<cstddef>
<cstdio>
<cstring>
<ctime>

Dan, sejak C ++ 11, juga di

<cstdlib>
<cwchar> 

Pertama-tama saya bertanya-tanya mengapa ini terjadi. Bukankah ini bertentangan dengan prinsip KERING ? Namun, pertanyaan saya adalah:

Manakah dari tajuk di atas yang harus saya sertakan untuk digunakan size_t? Apakah itu penting?

idclev 463035818
sumber
2
Buka file header yang sesuai dan temukan definisinya.
i486
35
@ i486 - Itu cara yang bagus untuk menulis kode non-portabel yang rapuh!
Sean
3
Header @PanagiotisKanavos C yang merupakan bagian dari pustaka standar C ++ dan mungkin tidak diduplikasi di salah satu tajuk 'true C ++' yang Anda duga. Sebenarnya apa maksudmu?
underscore_d
14
Saya selalu digunakan <cstddef>untukstd::size_t
Boiethios
4
@PanagiotisKanavos Tentu, secara umum itu adalah saran yang bagus, tetapi dalam hal ini tampaknya tidak relevan - karena tidak ada pengganti C ++ std::size_t, dan OP tidak menganjurkan penggunaan fungsi C lama, hanya mengamati kutipan tentang mereka yang membagikan typedef. Saya ragu siapa pun yang membaca utas ini akan disesatkan untuk menggunakan jenis / fungsi warisan karena ini, tetapi jika Anda ingin memastikan mereka tidak melakukannya, cukup adil!
underscore_d

Jawaban:

94

Dengan asumsi saya ingin meminimalkan fungsi dan tipe yang saya impor, saya akan menggunakannya cstddefkarena tidak mendeklarasikan fungsi apa pun dan hanya mendeklarasikan 6 jenis. Yang lain fokus pada domain tertentu (string, waktu, IO) yang mungkin tidak penting bagi Anda.

Perhatikan bahwa cstddefhanya jaminan untuk mendefinisikan std::size_t, yaitu, menentukan size_tdalam namespace std, meskipun mungkin memberikan nama ini juga di namespace global (efektif, biasa size_t).

Sebaliknya, stddef.h(yang juga merupakan header yang tersedia di C) jaminan untuk didefinisikan size_tdalam namespace global, dan mungkin juga menyediakan std::size_t.

Sean
sumber
3
Adakah jaminan bahwa size_tfrom cstddefadalah sama dan akan selalu sama dengan yang lain? Sepertinya harus ada file header umum dengan definisi umum seperti size_t...
SnakeDoc
1
@SnakeDoc dan seolah-olah secara ajaib, jawaban lain di sini telah mengamati persis yang terjadi, melalui header 'internal'.
underscore_d
5
@SnakeDoc Ya, dan header itu adalah cstddef.
pengguna253751
2
@SnakeDoc, siapa bilang mereka mendefinisikannya sendiri? Semua standar mengatakan apakah itu akan ditentukan setelah menyertakan tajuk tersebut, itu tidak mengatakan mereka semua harus mendefinisikan ulang. Mereka semua dapat menyertakan <cstddef>, atau semuanya dapat menyertakan beberapa header internal yang baru saja didefinisikan size_t.
Jonathan Wakely
1
Apakah csttddefjawabannya salah ketik? Mungkin cstddefmaksudnya?
Erik Sjölund
47

Faktanya, sinopsis (termasuk dalam standar C ++) dari beberapa header yang secara spesifik disertakan size_tserta header lebih lanjut menentukan jenisnya size_t(berdasarkan standar C karena <cX>header hanyalah <X.h>header ISO C dengan perubahan yang dicatat di mana penghapusan size_ttidak diindikasikan).

Standar C ++ bagaimanapun, mengacu <cstddef>pada definisistd::size_t

  • dalam 18.2 Jenis ,
  • dalam 5.3.3 Ukuran ,
  • dalam 3.7.4.2 Fungsi deallokasi (yang mengacu pada 18.2) dan
  • di 3.7.4.1 Fungsi alokasi (juga mengacu pada 18.2).

Oleh karena itu dan karena fakta bahwa <cstddef>hanya memperkenalkan tipe dan tidak ada fungsi, saya akan tetap menggunakan header ini untuk membuatnya std::size_ttersedia.


Perhatikan beberapa hal:

  1. Jenis std::size_tdapat diperoleh dengan decltypetanpa menyertakan header

    Jika Anda berencana untuk memperkenalkan typedef dalam kode Anda (yaitu karena Anda menulis wadah dan ingin menyediakan size_typetypedef) Anda dapat menggunakan global sizeof, sizeof...atau alignofoperator untuk menentukan tipe Anda tanpa menyertakan header sama sekali karena operator theose kembali std::size_tper definisi standar dan Anda dapat menggunakannya decltype:

    using size_type = decltype(alignof(char));
    
  2. std::size_ttidak terlihat secara global meskipun fungsi dengan std::size_targumennya ada.

    Fungsi alokasi dan deallocation global yang dideklarasikan secara implisit

    void* operator new(std::size_t);
    void* operator new[](std::size_t);
    void operator delete(void*);
    void operator delete[](void*);
    

    JANGAN memperkenalkan size_t, stdatau std::size_tdan

    merujuk ke stdatau std::size_tsalah format kecuali nama telah dinyatakan dengan menyertakan header yang sesuai.

  3. Pengguna tidak boleh mendefinisikan ulang std::size_tmeskipun dimungkinkan untuk memiliki beberapa typedef yang merujuk ke tipe yang sama di namespace yang sama.

    Meskipun, terjadinya beberapa definisi di size_tdalam stdbenar-benar valid sesuai 7.1.3 / 3 , tidak diperbolehkan untuk menambahkan deklarasi apa pun namespace stdsesuai 17.6.4.2.1 / 1 :

    Perilaku program C ++ tidak ditentukan jika program ini menambahkan deklarasi atau definisi ke namespace std atau ke namespace dalam namespace std kecuali ditentukan lain.

    Menambahkan typedef yang tepat untuk size_tnamespace tidak melanggar 7.1.3 tetapi melanggar 17.6.4.2.1 dan menyebabkan perilaku tidak terdefinisi.

    Klarifikasi: Cobalah untuk tidak salah menafsirkan 7.1.3 dan jangan menambahkan deklarasi atau definisi ke std(kecuali beberapa kasus spesialisasi template di mana typedef bukan spesialisasi template). Memperluasnamespace std

Pixelchemist
sumber
1
Anda melewatkan fakta bahwa typedef duplikat tidak memperkenalkan tipe baru. Itu hanya menambahkan typedef duplikat, yang sangat valid.
Maxim Egorushkin
@MaximEgorushkin: Saya tidak mengklaim bahwa menambahkan typedef definisi ulang ke stdtidak valid karena typedef duplikat adalah ilegal. Saya menyatakan bahwa itu ilegal karena Anda tidak boleh menambahkan definisi ke namespace std- tidak peduli apakah itu legal.
Pixelchemist
Apa yang berpotensi rusak, mengingat semua yang kami ketahui dari semua kutipan standar ini?
Maxim Egorushkin
12
@MaximEgorushkin: Apa saja. Itu tentang perilaku tidak terdefinisi, bukan? Titik yang mungkin bekerja atau bahkan titik yang tidak tidak melanggar pada setiap compiler sewenang-wenang tidak membuat perilaku program yang ditetapkan sesuai standar. Atau seperti yang dikatakan 'fredoverflow' dengan baik di sini : "Standar C ++ memiliki satu-satunya suara, titik."
Pixelchemist
Saya ingin Anda menggunakan pemikiran kritis Anda. Apa yang berpotensi pecah?
Maxim Egorushkin
9

Semua file header perpustakaan standar memiliki definisi yang sama; tidak masalah mana yang Anda masukkan dalam kode Anda sendiri. Di komputer saya, saya memiliki deklarasi berikut di _stddef.h. File ini disertakan oleh setiap file yang Anda daftarkan.

/*
   Define the size_t type in the std namespace if in C++ or globally if in C.
   If we're in C++, make the _SIZE_T macro expand to std::size_t
*/

#if !defined(_SIZE_T) && !defined(_SIZE_T_DEFINED)
#  define _SIZE_T_DEFINED
#if defined(_WIN64)
   typedef unsigned __int64 size_t;
#else
   typedef unsigned int size_t;
#endif
#  if defined(__cplusplus)
#    define _SIZE_T std::size_t
#  else
#    define _SIZE_T size_t
#  endif
#endif
vll
sumber
2
tidak yakin, tapi saya pikir itu penting untuk waktu kompilasi, bukan?
idclev 463035818
@ tobi303 bukan untuk pertanyaan khusus ini. Ya, Anda dapat menambahkan header yang lebih besar dari yang diperlukan, tetapi Anda sudah menambahkan header C dalam proyek C ++. Mengapa Anda membutuhkannya size_t?
Panagiotis Kanavos
Bukan ide yang baik untuk menggunakan OS macro sniffing untuk menentukan size_t. Anda dapat mendefinisikannya dengan lebih portabel sebagai using size_t = decltype( sizeof( 42 ) ). Tapi tidak perlu, karena <stddef.h>biayanya hampir nol.
Cheers and hth. - Alf
4

Anda dapat melakukannya tanpa header:

using size_t = decltype(sizeof(int));
using size_t = decltype(sizeof 1); //  The shortest is my favourite.
using size_t = decltype(sizeof "anything");

Ini karena standar C ++ membutuhkan:

Hasil dari sizeofdan sizeof...merupakan konstanta tipe std::size_t. [Catatan: std::size_tdidefinisikan dalam header standar <cstddef>(18.2). - catatan akhir]

Dengan kata lain, standar tersebut mensyaratkan:

static_assert(std::is_same<decltype(sizeof(int)), std::size_t>::value,
              "This never fails.");

Perhatikan juga, bahwa tidak masalah untuk membuat typedefdeklarasi ini di global dan di stdnamespace, selama typedefdeklarasi tersebut cocok dengan semua deklarasi lain dengan nama typedef yang sama (kesalahan kompiler dikeluarkan pada deklarasi yang tidak cocok).

Hal ini karena:

  • §7.1.3.1 Nama typedef tidak memperkenalkan tipe baru seperti yang dilakukan deklarasi kelas (9.1) atau deklarasi enum.

  • §7.1.3.3 Dalam lingkup non-kelas tertentu, penentu typedefdapat digunakan untuk mendefinisikan kembali nama jenis apa pun yang dideklarasikan dalam lingkup itu untuk merujuk ke jenis yang telah dirujuk.


Untuk para skeptis yang mengatakan bahwa ini merupakan penambahan tipe baru ke dalam namespace std, dan tindakan seperti itu secara eksplisit dilarang oleh standar, dan ini adalah UB dan itu saja; Saya harus mengatakan bahwa sikap ini berarti mengabaikan dan menyangkal pemahaman yang lebih dalam tentang masalah yang mendasarinya.

Larangan standar menambahkan deklarasi dan definisi baru ke dalam namespace stdkarena dengan melakukan itu pengguna dapat mengacaukan perpustakaan standar dan menembak seluruh kakinya. Untuk penulis standar, lebih mudah untuk membiarkan pengguna mengkhususkan beberapa hal tertentu dan melarang melakukan hal lain untuk ukuran yang baik, daripada melarang setiap hal yang tidak boleh dilakukan pengguna dan berisiko kehilangan sesuatu yang penting (dan kaki itu). Mereka melakukannya di masa lalu ketika mensyaratkan bahwa tidak ada kontainer standar yang dibuat dengan tipe yang tidak lengkap, padahal beberapa kontainer dapat melakukannya dengan baik (lihat Pustakawan Standar: Kontainer Tipe Tidak Lengkap oleh Matthew H. Austern ):

... Pada akhirnya, semuanya tampak terlalu keruh dan terlalu kurang dipahami; Komite standardisasi tidak berpikir ada pilihan lain kecuali mengatakan bahwa kontainer STL tidak seharusnya bekerja dengan tipe yang tidak lengkap. Untuk ukuran yang baik, kami menerapkan larangan itu ke seluruh perpustakaan standar juga.

... Dalam retrospeksi, sekarang teknologi lebih dipahami, keputusan itu pada dasarnya masih tampak benar. Ya, dalam beberapa kasus dimungkinkan untuk mengimplementasikan beberapa kontainer standar sehingga mereka dapat dibuat dengan tipe yang tidak lengkap - tetapi juga jelas bahwa dalam kasus lain itu akan sulit atau tidak mungkin. Kemungkinan besar tes pertama yang kami coba, gunakan std::vector, kebetulan merupakan salah satu kasus yang mudah.

Mengingat bahwa aturan bahasa std::size_tharus tepat decltype(sizeof(int)), melakukan namespace std { using size_t = decltype(sizeof(int)); }adalah salah satu hal yang tidak merusak apa pun.

Sebelum C ++ 11 tidak ada decltypedan dengan demikian tidak ada cara untuk mendeklarasikan tipe sizeofhasil dalam satu pernyataan sederhana tanpa melibatkan banyak template. size_tAlias ​​tipe yang berbeda pada arsitektur target yang berbeda, bagaimanapun, itu tidak akan menjadi solusi yang elegan untuk menambahkan tipe built-in baru hanya untuk hasil sizeof, dan tidak ada typedef bawaan standar. Oleh karena itu, solusi yang paling portabel saat itu adalah dengan meletakkan size_talias tipe di beberapa header dan dokumen tertentu itu.

Di C ++ 11 sekarang ada cara untuk menuliskan persyaratan standar yang tepat sebagai satu deklarasi sederhana.

Maxim Egorushkin
sumber
6
@Sean Apa yang Anda tulis tidak masuk akal.
Maxim Egorushkin
15
@MaximEgorushkin Separuh dari mereka tidak memahami kode ini ... ini bekerja dengan sempurna. Namun, saya tidak suka cara ini: lebih baik, imo, menyertakan header dan membiarkan standar yang menentukannya.
Boiethios
9
Teman-teman, setidaknya pelajari bahasa yang efektif sebelum Anda memberikan jawaban yang benar-benar sempurna.
Frédéric Hamidi
11
Tom berkata, "Ada 6 header perpustakaan standar yang mendefinisikan hal yang sama! Itu tidak masuk akal! Kita hanya perlu satu dan satu definisi size_t!" Satu menit kemudian, Mary berkata, "OMG! Ada 7 definisi dari size_tseluruh header perpustakaan standar dan header proyek yang sedang diedit Tom! Mungkin ada lebih banyak lagi di perpustakaan pihak ketiga!" xkcd.com/927
6
Meskipun itu adalah definisi yang mungkin size_t, ini tidak menjawab pertanyaan OP yang sebenarnya: seolah-olah saya meminta tajuk di mana FILEdinyatakan dan Anda akan menyarankan untuk menulis tajuk saya sendiri.
edmz