ฉันควรใส่ส่วนหัวใดสำหรับ "size_t"


98

ตามที่cppreference.com size_tกำหนดไว้ในหลายส่วนหัว ได้แก่

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

และตั้งแต่ C ++ 11 ก็เช่นกัน

<cstdlib>
<cwchar> 

ก่อนอื่นฉันสงสัยว่าทำไมถึงเป็นเช่นนี้ สิ่งนี้ไม่ขัดกับหลักการDRYหรือไม่? อย่างไรก็ตามคำถามของฉันคือ:

ซึ่งหนึ่งในหัวข้างต้นที่ผมควรจะรวมถึงการใช้งานsize_t? มันสำคัญหรือไม่?


2
เปิดไฟล์ส่วนหัวที่เกี่ยวข้องและค้นหาคำจำกัดความ
i486

35
@ i486 - เป็นวิธีที่ยอดเยี่ยมในการเขียนโค้ดที่ไม่สามารถพกพาได้!
Sean

3
@PanagiotisKanavos C ส่วนหัวที่เป็นส่วนหนึ่งของไลบรารีมาตรฐาน C ++ และอาจไม่ซ้ำกันในส่วนหัว 'True C ++' ที่คุณกล่าวหา ประเด็นของคุณคืออะไรกันแน่?
underscore_d

14
ฉันมักจะใช้<cstddef>สำหรับstd::size_t
Boiethios

4
@PanagiotisKanavos แน่นอนว่าโดยทั่วไปแล้วเป็นคำแนะนำที่ดี แต่ในกรณีนี้ดูเหมือนจะไม่เกี่ยวข้อง - เนื่องจากไม่มีการแทนที่ C ++ std::size_tและ OP ไม่ได้สนับสนุนการใช้ฟังก์ชัน C แบบเดิมเพียงแค่สังเกตคำพูดเกี่ยวกับพวกเขาที่แบ่งปัน typedef ฉันสงสัยว่าใครก็ตามที่อ่านหัวข้อนี้จะเข้าใจผิดในการใช้ประเภท / ฟังก์ชันดั้งเดิมเพราะเหตุนี้ แต่ถ้าคุณต้องการแน่ใจว่าพวกเขาไม่ทำก็ยุติธรรมพอ!
underscore_d

คำตอบ:


94

สมมติว่าฉันต้องการลดฟังก์ชั่นและประเภทที่ฉันกำลังนำเข้าฉันจะไปด้วยcstddefเพราะมันไม่ได้ประกาศฟังก์ชันใด ๆ และประกาศเพียง 6 ประเภทเท่านั้น ส่วนอื่น ๆ มุ่งเน้นไปที่โดเมนเฉพาะ (สตริงเวลา IO) ที่อาจไม่สำคัญสำหรับคุณ

โปรดทราบว่าcstddefรับประกันเฉพาะการกำหนดstd::size_tนั่นคือการกำหนดsize_tในเนมสเปซstdแม้ว่าอาจระบุชื่อนี้ในเนมสเปซส่วนกลางด้วย (มีประสิทธิภาพธรรมดาsize_t)

ในทางตรงกันข้ามstddef.h(ซึ่งเป็นส่วนหัวที่มีอยู่ใน C) รับประกันที่จะกำหนดsize_tใน namespace ทั่วโลกและอาจstd::size_tยังมี


3
มีการรับประกันหรือไม่ว่าsize_tจากcstddefที่เหมือนกันและจะเหมือนกับของอื่น ๆ ดูเหมือนว่าควรมีไฟล์ส่วนหัวทั่วไปที่มีคำจำกัดความทั่วไปเช่นsize_t...
SnakeDoc

1
@SnakeDoc และราวกับว่ามีเวทมนตร์คำตอบอื่นที่นี่ได้สังเกตเห็นสิ่งที่เกิดขึ้นผ่านส่วนหัว 'ภายใน'
underscore_d

5
@SnakeDoc cstddefใช่และที่ส่วนหัว
user253751

2
@SnakeDoc ใครบอกว่าพวกเขากำหนดของตัวเอง? มาตรฐานทั้งหมดระบุว่าจะถูกกำหนดหลังจากรวมส่วนหัวเหล่านั้นแล้ว แต่ก็ไม่ได้บอกว่าทั้งหมดต้องกำหนดใหม่ ทั้งหมดนี้สามารถรวม<cstddef>หรืออาจรวมส่วนหัวภายในบางส่วนที่กำหนดไว้size_tก็ได้
Jonathan Wakely

1
คือcsttddefในคำตอบที่พิมพ์ผิดหรือไม่? อาจจะ cstddefมีความหมาย?
Erik Sjölund

47

ในความเป็นจริงบทสรุป (รวมอยู่ในมาตรฐาน C ++) ของส่วนหัวหลายส่วนsize_tโดยเฉพาะรวมถึงส่วนหัวเพิ่มเติมกำหนดประเภทsize_t(ตามมาตรฐาน C เนื่องจาก<cX>ส่วนหัวเป็นเพียง<X.h>ส่วนหัวISO C ที่มีการเปลี่ยนแปลงที่สังเกตเห็นซึ่งsize_tไม่ได้ระบุการลบออก)

อย่างไรก็ตามมาตรฐาน C ++ หมายถึง<cstddef>คำจำกัดความของstd::size_t

  • ใน18.2 ประเภท ,
  • ใน5.3.3 Sizeof ,
  • ใน3.7.4.2 ฟังก์ชัน Deallocation (ซึ่งอ้างถึง 18.2) และ
  • ใน3.7.4.1 ฟังก์ชันการจัดสรร (หมายถึง 18.2)

ดังนั้นและเนื่องจากการ<cstddef>แนะนำเฉพาะประเภทและไม่มีฟังก์ชันใด ๆ ฉันจึงใช้ส่วนหัวนี้เพื่อให้std::size_tพร้อมใช้งาน


สังเกตบางสิ่ง:

  1. ประเภทของstd::size_tสามารถหาได้โดยใช้decltypeโดยไม่รวมส่วนหัว

    หากคุณกำลังวางแผนที่จะแนะนำ typedef ในรหัสของคุณอยู่แล้ว (เช่นเพราะคุณเขียนภาชนะและต้องการที่จะให้size_typetypedef) คุณสามารถใช้ทั่วโลกsizeof, sizeof...หรือalignofผู้ประกอบการเพื่อกำหนดประเภทของคุณโดยไม่รวมถึงส่วนหัวใด ๆ ที่ทุกคนตั้งแต่ผู้ประกอบการ theose กลับstd::size_tต่อ ความคมชัดมาตรฐานและคุณสามารถใช้decltypeกับ:

    using size_type = decltype(alignof(char));
    
  2. std::size_tไม่สามารถมองเห็นได้ทั่วโลกแม้ว่าฟังก์ชันที่มีstd::size_tอาร์กิวเมนต์จะอยู่

    ฟังก์ชันการจัดสรรและการยกเลิกการจัดสรรส่วนกลางที่ประกาศโดยปริยาย

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

    ไม่แนะนำsize_t, stdหรือstd::size_tและ

    อ้างถึงstdหรือstd::size_tมีรูปแบบไม่ถูกต้องเว้นแต่จะมีการประกาศชื่อโดยรวมส่วนหัวที่เหมาะสม

  3. ผู้ใช้ไม่สามารถกำหนดนิยามใหม่ได้std::size_tแม้ว่าจะเป็นไปได้ที่จะมี typedef หลายตัวที่อ้างถึงประเภทเดียวกันในเนมสเปซเดียวกัน

    แม้ว่าการเกิดขึ้นของคำจำกัดความsize_tภายในหลายคำstdจะถูกต้องสมบูรณ์ตาม7.1.3 / 3แต่ไม่อนุญาตให้เพิ่มการประกาศใด ๆnamespace stdตาม17.6.4.2.1 / 1 :

    ลักษณะการทำงานของโปรแกรม C ++ ไม่ได้กำหนดไว้หากเพิ่มการประกาศหรือคำจำกัดความให้กับ namespace std หรือใน namespace ภายใน namespace std เว้นแต่จะระบุไว้เป็นอย่างอื่น

    การเพิ่ม typedef ที่เหมาะสมสำหรับsize_tเนมสเปซจะไม่ละเมิด7.1.3แต่เป็นการละเมิด17.6.4.2.1และนำไปสู่พฤติกรรมที่ไม่ได้กำหนด

    คำชี้แจง: พยายามอย่าตีความ7.1.3ผิดพลาดและอย่าเพิ่มการประกาศหรือคำจำกัดความลงในstd(ยกเว้นกรณีเฉพาะเทมเพลตบางกรณีที่ typedef ไม่ใช่ความเชี่ยวชาญพิเศษของเทมเพลต) การขยายไฟล์namespace std


1
คุณพลาดความจริงที่ว่า typedef ที่ซ้ำกันไม่ได้แนะนำประเภทใหม่ เป็นเพียงการเพิ่ม typedef ที่ซ้ำกันซึ่งใช้ได้อย่างสมบูรณ์
Maxim Egorushkin

@MaximEgorushkin: ฉันไม่ได้อ้างว่าการเพิ่มการกำหนด typedef ใหม่stdเป็นไม่ถูกต้องเพราะตัวพิมพ์ที่ซ้ำกันนั้นผิดกฎหมาย ฉันระบุว่าผิดกฎหมายเพราะคุณไม่สามารถเพิ่มคำจำกัดความเข้าไปได้namespace stdไม่ว่าจะเป็นกฎหมายก็ตาม
Pixelchemist

อะไรที่อาจทำลายได้จากทั้งหมดที่เรารู้จากคำพูดมาตรฐานเหล่านี้ทั้งหมด?
Maxim Egorushkin

12
@MaximEgorushkin: อะไรก็ได้ นั่นคือพฤติกรรมที่ไม่ได้กำหนดไว้แล้วใช่หรือไม่? จุดที่มันอาจจะทำงานหรือแม้กระทั่งจุดที่มันไม่ได้ทำลายบนเรียบเรียงโดยพลการใด ๆ ไม่ได้ทำให้การทำงานของโปรแกรมที่กำหนดไว้ตามมาตรฐาน หรืออย่างที่ "fredoverflow" ใส่ไว้ตรงนี้ : "มาตรฐาน C ++ มีการโหวตเพียงช่วงเวลา"
Pixelchemist

ขอให้คุณใช้วิจารณญาณ อะไรที่อาจทำลาย?
Maxim Egorushkin

9

ไฟล์ส่วนหัวของไลบรารีมาตรฐานทั้งหมดมีความหมายเดียวกัน ไม่สำคัญว่าคุณจะใส่รหัสใดในรหัสของคุณเอง ในคอมพิวเตอร์ของฉันฉันมีคำประกาศต่อไปนี้ใน_stddef.h. ไฟล์นี้รวมอยู่ในทุกไฟล์ที่คุณระบุไว้

/*
   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

2
ไม่แน่ใจ แต่ฉันคิดว่ามันสำคัญสำหรับเวลาในการรวบรวมไม่ใช่เหรอ?
idclev 463035818

@ tobi303 ไม่ใช่สำหรับคำถามเฉพาะนี้ ใช่คุณอาจเพิ่มส่วนหัวที่ใหญ่กว่าที่จำเป็น แต่คุณได้เพิ่มส่วนหัว C ในโปรเจ็กต์ C ++ แล้ว ทำไมคุณถึงต้องการsize_tตั้งแต่แรก?
Panagiotis Kanavos

มันไม่ได้เป็นความคิดที่ดีที่จะใช้ OS size_tแมโครดมกลิ่นในการกำหนด คุณสามารถกำหนดมันมากขึ้น portably using size_t = decltype( sizeof( 42 ) )เป็น แต่ไม่จำเป็นเพราะ<stddef.h>แทบไม่มีต้นทุน
ไชโยและ hth - Alf

4

คุณสามารถทำได้โดยไม่ต้องมีส่วนหัว:

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

เนื่องจากมาตรฐาน C ++ ต้องการ:

ผลมาจากsizeofและเป็นค่าคงที่ของประเภทsizeof... std::size_t[หมายเหตุ: std::size_tกำหนดไว้ในส่วนหัวมาตรฐาน<cstddef>(18.2) - หมายเหตุ]

กล่าวอีกนัยหนึ่งมาตรฐานต้องการ:

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

นอกจากนี้โปรดทราบว่าtypedefการประกาศนี้เป็นสิ่งที่ดีอย่างสมบูรณ์ในทั่วโลกและในstdเนมสเปซตราบเท่าที่ตรงกับtypedefการประกาศอื่น ๆ ทั้งหมดที่มีชื่อ typedefเดียวกัน(ข้อผิดพลาดของคอมไพเลอร์จะออกในการประกาศที่ไม่ตรงกัน)

นี้เป็นเพราะ:

  • §7.1.3.1 typedef-nameไม่แนะนำประเภทใหม่แบบที่การประกาศคลาส (9.1) หรือการประกาศ enum ทำ

  • §7.1.3.3ในขอบเขตที่ไม่ใช่คลาสที่typedefกำหนดตัวระบุสามารถใช้เพื่อกำหนดชื่อของประเภทใด ๆ ที่ประกาศในขอบเขตนั้นใหม่เพื่ออ้างถึงประเภทที่อ้างถึงอยู่แล้ว


สำหรับผู้คลางแคลงที่บอกว่านี่เป็นการเพิ่มประเภทใหม่ในเนมสเปซstdและการกระทำดังกล่าวเป็นสิ่งต้องห้ามอย่างชัดเจนโดยมาตรฐานและนี่คือ UB และนั่นคือทั้งหมดที่อยู่ในนั้น ฉันต้องบอกว่าทัศนคตินี้เป็นการเพิกเฉยและปฏิเสธความเข้าใจที่ลึกซึ้งยิ่งขึ้นเกี่ยวกับปัญหาพื้นฐาน

มาตรฐานห้ามเพิ่มการประกาศและคำจำกัดความใหม่ลงในเนมสเปซstdเนื่องจากการทำเช่นนั้นผู้ใช้อาจทำให้ไลบรารีมาตรฐานยุ่งเหยิงและทำให้ขาของเขาหลุดออกไปทั้งหมด สำหรับนักเขียนมาตรฐานมันง่ายกว่าที่จะปล่อยให้ผู้ใช้เชี่ยวชาญบางสิ่งบางอย่างและห้ามทำสิ่งอื่นใดเพื่อการวัดที่ดีแทนที่จะห้ามทุกสิ่งที่ผู้ใช้ไม่ควรทำและเสี่ยงต่อการพลาดบางสิ่งที่สำคัญ (และขานั้น) พวกเขาเคยทำมาแล้วในอดีตเมื่อกำหนดให้ไม่ต้องสร้างคอนเทนเนอร์มาตรฐานด้วยประเภทที่ไม่สมบูรณ์ในขณะที่ในความเป็นจริงคอนเทนเนอร์บางอย่างสามารถทำได้ดี (ดูThe Standard Librarian: Containers of Incomplete types by Matthew H. Austern ):

... ในที่สุดทุกอย่างก็ดูมืดมนเกินไปและเข้าใจไม่ดีเกินไป คณะกรรมการกำหนดมาตรฐานไม่คิดว่าจะมีทางเลือกอื่นนอกจากบอกว่าคอนเทนเนอร์ STL ไม่ควรทำงานกับประเภทที่ไม่สมบูรณ์ เพื่อการวัดที่ดีเราได้ใช้ข้อห้ามนั้นกับห้องสมุดมาตรฐานอื่น ๆ ด้วย

... เมื่อมองย้อนกลับไปตอนนี้เมื่อเข้าใจเทคโนโลยีมากขึ้นแล้วการตัดสินใจนั้นยังดูเหมือนถูกต้อง ใช่ในบางกรณีเป็นไปได้ที่จะใช้คอนเทนเนอร์มาตรฐานบางส่วนเพื่อให้สามารถสร้างอินสแตนซ์กับประเภทที่ไม่สมบูรณ์ได้ แต่ก็เป็นที่ชัดเจนว่าในกรณีอื่น ๆ อาจเป็นเรื่องยากหรือเป็นไปไม่ได้ ส่วนใหญ่เป็นโอกาสที่การทดสอบครั้งแรกที่เราลองใช้std::vectorนั้นเป็นหนึ่งในกรณีที่ง่าย

เนื่องจากกฎของภาษาต้องstd::size_tเป็นไปอย่างแน่นอนdecltype(sizeof(int))การทำnamespace std { using size_t = decltype(sizeof(int)); }จึงเป็นหนึ่งในสิ่งที่ไม่ผิดอะไรเลย

ก่อนหน้า C ++ 11 ไม่มีdecltypeและด้วยเหตุนี้จึงไม่มีวิธีการประกาศประเภทของsizeofผลลัพธ์ในคำสั่งง่ายๆเพียงคำสั่งเดียวโดยไม่ต้องมีเทมเพลตมากมายที่เกี่ยวข้อง size_tนามแฝงประเภทต่างๆบนสถาปัตยกรรมเป้าหมายที่แตกต่างกันอย่างไรก็ตามมันจะไม่ใช่วิธีการแก้ปัญหาที่หรูหราในการเพิ่มประเภทในตัวใหม่เพียงเพื่อผลลัพธ์sizeofและไม่มีตัวพิมพ์มาตรฐานในตัว ดังนั้นวิธีแก้ปัญหาแบบพกพาที่สุดในขณะนั้นคือการใส่size_tนามแฝงประเภทในส่วนหัวที่เฉพาะเจาะจงและจัดทำเอกสารนั้น

ใน C ++ 11 ตอนนี้มีวิธีการเขียนข้อกำหนดที่แน่นอนของมาตรฐานนั้นเป็นการประกาศง่ายๆ


6
@Sean สิ่งที่คุณเขียนไม่สมเหตุสมผลเลย
Maxim Egorushkin

15
@MaximEgorushkin ครึ่งหนึ่งไม่เข้าใจรหัสนี้ ... มันทำงานได้อย่างสมบูรณ์ อย่างไรก็ตามฉันไม่ชอบวิธีนี้: เป็นการดีกว่า imo ที่จะรวมส่วนหัวและปล่อยให้มาตรฐานกำหนด
Boiethios

9
อย่างน้อยพวกเขาเรียนรู้ภาษาที่เปล่งออกมาก่อนที่คุณจะลงคะแนนคำตอบที่ถูกต้องสมบูรณ์
Frédéric Hamidi

11
ทอมกล่าวว่า "มี 6 ส่วนหัวของไลบรารีมาตรฐานที่กำหนดสิ่งเดียวกันนั่นคือบ้า! เราต้องการคำจำกัดความคำเดียวsize_t!" หนึ่งนาทีต่อมา Mary กล่าวว่า "OMG! มีคำจำกัดความ 7 คำsize_tในส่วนหัวของไลบรารีมาตรฐานและส่วนหัวของโปรเจ็กต์ที่ Tom กำลังแก้ไขอยู่อาจมีมากกว่านี้ในไลบรารีของบุคคลที่สาม!" xkcd.com/927

6
แม้ว่าจะเป็นคำจำกัดความที่เป็นไปได้size_tแต่ก็ไม่ได้ตอบคำถามที่แท้จริงของ OP: เหมือนกับว่าฉันขอส่วนหัวที่FILEประกาศไว้และคุณจะแนะนำให้เขียนของตัวเอง
edmz
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.