Tôi nên bao gồm tiêu đề nào cho `size_t`?


95

Theo cppreference.com size_t được định nghĩa trong một số tiêu đề, cụ thể là

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

Và, kể từ C ++ 11, cũng trong

<cstdlib>
<cwchar> 

Trước hết tôi tự hỏi tại sao lại như vậy. Điều này không mâu thuẫn với nguyên tắc DRY phải không? Tuy nhiên, câu hỏi của tôi là:

Tôi nên bao gồm một trong các tiêu đề trên để sử dụng size_t? Nó có vấn đề gì không?


1
Mở tệp tiêu đề tương ứng và tìm định nghĩa.
i486

33
@ i486 - Đó là một cách tuyệt vời để viết mã không di động dễ gãy!
Sean

3
@PanagiotisKanavos tiêu đề C là một phần của thư viện tiêu chuẩn C ++ và có thể không bị trùng lặp trong bất kỳ tiêu đề nào được cho là 'C ++ thực' của bạn. Chính xác thì quan điểm của bạn là gì?
underscore_d

14
Tôi luôn sử dụng <cstddef>chostd::size_t
Boiethios

4
@PanagiotisKanavos Chắc chắn đó là lời khuyên tốt, nhưng trong trường hợp này, nó có vẻ không liên quan - vì không có C ++ thay thế std::size_tvà OP không ủng hộ việc sử dụng các hàm C cũ, chỉ cần quan sát trích dẫn về việc họ chia sẻ typedef. Tôi nghi ngờ rằng bất cứ ai đọc chủ đề này sẽ bị nhầm lẫn khi sử dụng các loại / chức năng kế thừa vì điều này, nhưng nếu bạn muốn chắc chắn rằng họ không, thì đủ công bằng!
underscore_d

Câu trả lời:


90

Giả sử tôi muốn giảm thiểu các chức năng và kiểu nhập tôi sẽ sử dụng cstddef vì nó không khai báo bất kỳ hàm nào và chỉ khai báo 6 kiểu. Những người khác tập trung vào các miền cụ thể (chuỗi, thời gian, IO) có thể không quan trọng với bạn.

Lưu ý rằng cstddefchỉ đảm bảo xác định std::size_t, nghĩa là xác định size_ttrong không gian tên std, mặc dù nó có thể cung cấp tên này trong không gian tên chung (hiệu quả, đơn giản size_t).

Ngược lại, stddef.h(cũng là một tiêu đề có sẵn trong C) đảm bảo xác định size_ttrong không gian tên chung và cũng có thể cung cấp std::size_t.


3
Có bất kỳ đảm bảo rằng size_tfrom cstddeflà giống nhau và sẽ luôn giống như những cái khác không? Có vẻ như nên có một tệp tiêu đề chung với các định nghĩa phổ biến như size_t...
SnakeDoc

1
@SnakeDoc và như thể bằng phép thuật, một câu trả lời khác ở đây đã quan sát chính xác điều đó đang xảy ra, thông qua tiêu đề 'nội bộ'.
underscore_d

5
@SnakeDoc Có, và tiêu đề đó là cstddef.
user253751 14/04

2
@SnakeDoc, ai nói rằng họ tự định nghĩa? Tất cả những gì tiêu chuẩn nói là nó sẽ được xác định sau khi bao gồm những tiêu đề đó, nó không nói rằng tất cả đều phải xác định lại nó. Tất cả chúng đều có thể bao gồm <cstddef>hoặc tất cả chúng đều có thể bao gồm một số tiêu đề nội bộ chỉ định nghĩa size_t.
Jonathan Wakely

1
csttddeftrong câu trả lời một lỗi đánh máy? Có thể cstddefcó nghĩa là?
Erik Sjölund

46

Trên thực tế, phần tóm tắt (bao gồm trong tiêu chuẩn C ++) của một số tiêu đề cụ thể bao gồm size_tcũng như các tiêu đề khác xác định loại size_t(dựa trên tiêu chuẩn C vì <cX>tiêu đề chỉ là tiêu đề ISO C <X.h>với những thay đổi được lưu ý mà việc loại bỏ size_tkhông được chỉ định).

Tuy nhiên, tiêu chuẩn C ++ đề cập đến <cstddef>định nghĩa củastd::size_t

  • trong 18.2 Loại ,
  • trong 5.3.3 Sizeof ,
  • trong 3.7.4.2 Các chức năng phân bổ (đề cập đến 18.2) và
  • trong 3.7.4.1 Các chức năng phân bổ (cũng tham khảo 18.2).

Do đó, và vì thực tế là <cstddef>chỉ giới thiệu các loại và không có chức năng, tôi sẽ bám vào tiêu đề này để std::size_tcung cấp.


Lưu ý một số điều:

  1. Loại std::size_tcó thể có được bằng cách sử dụng decltypemà không bao gồm tiêu đề

    Nếu bạn lên kế hoạch đang giới thiệu một typedef trong mã của bạn anyway (tức là vì bạn viết một container và muốn cung cấp một size_typetypedef), bạn có thể sử dụng toàn cầu sizeof, sizeof...hoặc alignofkhai thác để xác định kiểu của bạn mà không bao gồm bất kỳ tiêu đề ở tất cả vì theose khai thác trở lại std::size_tmỗi định nghĩa tiêu chuẩn và bạn có thể sử dụng decltypechúng:

    using size_type = decltype(alignof(char));
  2. std::size_tkhông hiển thị trên toàn cầu mặc dù các hàm có std::size_tđối số.

    Các hàm phân bổ và phân bổ toàn cục được khai báo ngầm

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

    KHÔNG giới thiệu size_t, stdhoặcstd::size_t

    đề cập đến stdhoặc std::size_tkhông hợp lệ trừ khi tên đã được khai báo bằng cách bao gồm tiêu đề thích hợp.

  3. Người dùng không được xác định lại std::size_t mặc dù có thể có nhiều typedef tham chiếu đến cùng một loại trong cùng một không gian tên.

    Mặc dù, sự xuất hiện của nhiều định nghĩa của size_tbên trong stdlà hoàn toàn hợp lệ theo 7.1.3 / 3 , không được phép thêm bất kỳ khai báo nào vào namespace stdtheo 17.6.4.2.1 / 1 :

    Hành vi của một chương trình C ++ là không xác định nếu nó thêm các khai báo hoặc định nghĩa vào không gian tên std hoặc vào một không gian tên trong std không gian tên trừ khi được chỉ định khác.

    Việc thêm một typedef thích hợp cho size_tkhông gian tên không vi phạm 7.1.3 nhưng nó vi phạm 17.6.4.2.1 và dẫn đến hành vi không xác định.

    Làm rõ: Cố gắng không hiểu sai 7.1.3 và không thêm khai báo hoặc định nghĩa vào std(ngoại trừ một số trường hợp chuyên môn hóa mẫu trong đó typedef không phải là chuyên môn hóa mẫu). Mở rộngnamespace std


1
Bạn bỏ lỡ thực tế là một typedef trùng lặp không giới thiệu một kiểu mới. Nó chỉ thêm một typedef trùng lặp, hoàn toàn hợp lệ.
Maxim Egorushkin

@MaximEgorushkin: Tôi không khẳng định rằng việc thêm định nghĩa lại typedef vào stdlà không hợp lệ vì các typedef trùng lặp là bất hợp pháp. Tôi tuyên bố rằng điều đó là bất hợp pháp vì bạn chỉ có thể không thêm các định nghĩa vào namespace std- bất kể chúng có hợp pháp hay không.
Pixelchemist

Điều gì có thể phá vỡ, với tất cả những gì chúng ta biết từ tất cả những câu trích dẫn tiêu chuẩn này?
Maxim Egorushkin

12
@MaximEgorushkin: Bất cứ điều gì. Đó là những gì về hành vi không xác định, phải không? Vấn đề là nó có thể hoạt động hoặc thậm chí các điểm đó nó không không phá vỡ trên bất kỳ trình biên dịch tùy ý không làm cho hành vi của chương trình được xác định theo tiêu chuẩn. Hay như 'fredoverflow' đã diễn đạt một cách độc đáo ở đây : "Tiêu chuẩn C ++ có phiếu bầu duy nhất, dấu chấm ."
Pixelchemist

Tôi muốn bạn sử dụng tư duy phản biện của bạn. Điều gì có thể phá vỡ?
Maxim Egorushkin

9

Tất cả các tệp tiêu đề thư viện tiêu chuẩn có cùng một định nghĩa; không quan trọng bạn đưa cái nào vào mã của riêng mình. Trên máy tính của tôi, tôi có khai báo sau _stddef.h. Tệp này được bao gồm bởi mọi tệp bạn đã liệt kê.

/*
   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
không chắc chắn, nhưng tôi nghĩ nó có vấn đề về thời gian biên dịch, không?
idclev 463035818

@ tobi303 không dành cho câu hỏi cụ thể này. Có, bạn có thể thêm tiêu đề lớn hơn mức cần thiết, nhưng sau đó bạn đã thêm tiêu đề C trong một dự án C ++. Tại sao bạn cần size_tngay từ đầu?
Panagiotis Kanavos

Bạn không nên sử dụng tính năng dò tìm macro hệ điều hành để xác định size_t. Bạn có thể định nghĩa nó di động hơn là using size_t = decltype( sizeof( 42 ) ). Nhưng không cần thiết, vì <stddef.h>chi phí gần như bằng không.
Chúc mừng và hth. - Alf

4

Bạn có thể làm mà không có tiêu đề:

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

Điều này là do tiêu chuẩn C ++ yêu cầu:

Kết quả của sizeofsizeof...là một hằng số kiểu std::size_t. [Lưu ý: std::size_tđược định nghĩa trong tiêu đề chuẩn <cstddef>(18.2). - ghi chú cuối]

Nói cách khác, tiêu chuẩn yêu cầu:

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

Cũng lưu ý rằng việc typedefkhai báo này trong toàn cục và trong stdkhông gian tên là hoàn toàn ổn , miễn là nó khớp với tất cả các typedefkhai báo khác của cùng một typedef-name (lỗi trình biên dịch được đưa ra trên các khai báo không khớp).

Điều này là do:

  • §7.1.3.1 Tên typedef không giới thiệu kiểu mới như cách khai báo lớp (9.1) hoặc khai báo enum.

  • §7.1.3.3 Trong một phạm vi phi lớp nhất định, một bộ typedefxác định có thể được sử dụng để xác định lại tên của bất kỳ kiểu nào được khai báo trong phạm vi đó để tham chiếu đến kiểu mà nó đã tham chiếu đến.


Đối với những người hoài nghi nói rằng điều này tạo thành một sự bổ sung của một kiểu mới vào không gian tên std , và một hành động như vậy bị nghiêm cấm rõ ràng theo tiêu chuẩn, và đây là UB và đó là tất cả những gì ở đó; Tôi phải nói rằng thái độ này dẫn đến việc phớt lờ và phủ nhận sự hiểu biết sâu sắc hơn về các vấn đề cơ bản.

Tiêu chuẩn cấm thêm các khai báo và định nghĩa mới vào không gian tên stdbởi vì làm như vậy người dùng có thể làm xáo trộn thư viện tiêu chuẩn và làm mất toàn bộ chân của họ. Đối với những người viết tiêu chuẩn, dễ dàng hơn để cho phép người dùng chuyên môn hóa một vài thứ cụ thể và cấm làm bất cứ điều gì khác để có biện pháp tốt, hơn là cấm mọi thứ mà người dùng không nên làm và có nguy cơ bỏ lỡ điều gì đó quan trọng (và điều đó). Trước đây họ đã làm điều đó khi yêu cầu rằng không có thùng chứa tiêu chuẩn nào được khởi tạo với loại không hoàn chỉnh, trong khi trên thực tế, một số thùng chứa cũng có thể làm được (xem Thư viện tiêu chuẩn: Các thùng chứa loại không đầy đủ của Matthew H. Austern ):

... Cuối cùng, tất cả dường như quá âm u và quá kém hiểu biết; ủy ban tiêu chuẩn hóa không cho rằng có bất kỳ sự lựa chọn nào ngoại trừ việc nói rằng các thùng chứa STL không được cho là hoạt động với các loại không hoàn chỉnh. Để có biện pháp tốt, chúng tôi cũng đã áp dụng lệnh cấm đó cho phần còn lại của thư viện tiêu chuẩn.

... Nhìn lại, bây giờ công nghệ đã được hiểu rõ hơn, quyết định đó về cơ bản vẫn có vẻ đúng. Có, trong một số trường hợp, có thể triển khai một số vùng chứa tiêu chuẩn để chúng có thể được khởi tạo với các loại không hoàn chỉnh - nhưng cũng rõ ràng rằng trong các trường hợp khác, điều đó sẽ khó hoặc không thể. Phần lớn cơ hội là bài kiểm tra đầu tiên chúng tôi đã thử, sử dụng std::vector, tình cờ là một trong những trường hợp dễ dàng.

Cho rằng các quy tắc ngôn ngữ yêu cầu std::size_tphải chính xác decltype(sizeof(int)), làm namespace std { using size_t = decltype(sizeof(int)); }là một trong những điều không vi phạm bất cứ điều gì.

Trước C ++ 11, không có decltypevà do đó không có cách nào để khai báo kiểu sizeofkết quả trong một câu lệnh đơn giản mà không có nhiều mẫu liên quan. size_tcác kiểu bí danh khác nhau trên các kiến ​​trúc đích khác nhau, tuy nhiên, sẽ không phải là một giải pháp thanh lịch nếu thêm một kiểu tích hợp mới chỉ cho kết quả sizeofvà không có typedef tích hợp chuẩn nào. Do đó, giải pháp di động nhất vào thời điểm đó là đặt size_tbí danh kiểu vào một số tiêu đề và tài liệu cụ thể.

Trong C ++ 11 bây giờ có một cách để viết ra yêu cầu chính xác của tiêu chuẩn như một khai báo đơn giản.


6
@Sean Những gì bạn đã viết không có ý nghĩa gì cả.
Maxim Egorushkin

15
@MaximEgorushkin Một nửa trong số họ không hiểu mã này ... nó hoạt động hoàn hảo. Tuy nhiên, tôi không thích cách này: tốt hơn là imo nên bao gồm tiêu đề và để tiêu chuẩn xác định nó.
Boiethios

9
Các bạn, ít nhất hãy học ngôn ngữ hiệu quả trước khi từ chối những câu trả lời hoàn toàn đúng.
Frédéric Hamidi

11
Tom nói, "Có 6 tiêu đề thư viện tiêu chuẩn xác định cùng một thứ! Điều đó thật điên rồ! Chúng tôi cần một và chỉ một định nghĩa về size_t!" Một phút sau, Mary nói, "OMG! Có 7 định nghĩa về size_ttiêu đề thư viện tiêu chuẩn và tiêu đề dự án mà Tom đang chỉnh sửa! Có lẽ còn nhiều định nghĩa hơn trong các thư viện của bên thứ 3!" xkcd.com/927

6
Mặc dù đó là một định nghĩa khả thi size_t, nhưng điều này không trả lời câu hỏi thực sự của OP: nó giống như thể tôi yêu cầu tiêu đề FILEđược khai báo ở đâu và bạn đề nghị viết một tiêu đề của riêng tôi.
edmz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.