Tại sao std :: initializer_list không phải là một ngôn ngữ được tích hợp sẵn?


95

Tại sao không std::initializer_list ngôn ngữ cốt lõi không được tích hợp sẵn?

Đối với tôi, có vẻ như đó là một tính năng khá quan trọng của C ++ 11 và nó không có từ khóa dành riêng (hoặc một cái gì đó tương tự).

Thay vào đó, initializer_listchỉ là một lớp mẫu từ thư viện tiêu chuẩn có ánh xạ ngầm, đặc biệt từ cú pháp giằng-init-list {...} mới do trình biên dịch xử lý.

Thoạt nghĩ, giải pháp này khá hacky .

Đây có phải là cách mà các bổ sung mới cho ngôn ngữ C ++ hiện sẽ được thực hiện: bởi các vai trò ngầm định của một số lớp mẫu chứ không phải bởi ngôn ngữ cốt lõi ?


Vui lòng xem xét các ví dụ sau:

   widget<int> w = {1,2,3}; //this is how we want to use a class

tại sao một lớp mới được chọn:

   widget( std::initializer_list<T> init )

thay vì sử dụng một cái gì đó tương tự với bất kỳ ý tưởng nào sau đây:

   widget( T[] init, int length )  // (1)
   widget( T... init )             // (2)
   widget( std::vector<T> init )   // (3)
  1. một mảng cổ điển, bạn có thể thêm constở đây và ở đó
  2. ba dấu chấm đã tồn tại trong ngôn ngữ (var-args, bây giờ là các mẫu biến thể), tại sao không sử dụng lại cú pháp (và tạo cảm giác nó được tích hợp sẵn )
  3. chỉ một vùng chứa hiện có, có thể thêm const&

Tất cả chúng đã là một phần của ngôn ngữ. Tôi chỉ viết 3 ý tưởng đầu tiên của mình, tôi chắc chắn rằng còn nhiều cách tiếp cận khác.


26
Ủy ban tiêu chuẩn ghét thêm các từ khóa mới!
Alex Chamberlain

11
Điều này tôi hiểu, nhưng có nhiều khả năng làm thế nào để mở rộng ngôn ngữ ( từ khóa chỉ là một ví dụ )
emesx

10
std::array<T>không phải là 'một phần của ngôn ngữ' hơn std::initializer_list<T>. Và đây không phải là những thành phần thư viện duy nhất mà ngôn ngữ dựa vào. Xem new/ delete,, type_infocác loại ngoại lệ khác nhau size_t, v.v.
bames53 Ngày

6
@Elmes: Tôi đã đề xuất const T(*)[N], bởi vì điều đó hoạt động rất giống với cách std::initializer_listhoạt động.
Mooing Duck

1
Điều này trả lời tại sao std::arrayhoặc một mảng có kích thước tĩnh là những lựa chọn thay thế ít được mong muốn hơn.
boycy

Câu trả lời:


48

Đã có các ví dụ về các tính năng ngôn ngữ "cốt lõi" trả về các kiểu được xác định trong stdkhông gian tên. typeidtrả về std::type_infovà (có lẽ kéo dài một điểm) sizeoftrả về std::size_t.

Trong trường hợp trước đây, bạn đã cần phải bao gồm một tiêu đề chuẩn để sử dụng tính năng được gọi là "ngôn ngữ cốt lõi" này.

Bây giờ, đối với danh sách trình khởi tạo, điều đó xảy ra là không cần từ khóa để tạo đối tượng, cú pháp là dấu ngoặc nhọn phân biệt ngữ cảnh. Bên cạnh đó, nó giống như type_info. Cá nhân tôi không nghĩ rằng sự vắng mặt của một từ khóa làm cho nó trở nên "hacky" hơn. Có lẽ hơi ngạc nhiên hơn một chút, nhưng hãy nhớ rằng mục tiêu là cho phép cùng một cú pháp khởi tạo dấu ngoặc nhọn đã được phép cho các tổng hợp.

Vì vậy, có, bạn có thể mong đợi nhiều hơn về nguyên tắc thiết kế này trong tương lai:

  • nếu có nhiều dịp hơn mà có thể giới thiệu các tính năng mới mà không có từ khóa mới thì ủy ban sẽ thực hiện chúng.
  • nếu các tính năng mới yêu cầu các loại phức tạp, thì các loại đó sẽ được đặt trong stdthay vì ở dạng nội trang.

Vì thế:

  • nếu một tính năng mới yêu cầu một loại phức tạp và có thể được giới thiệu mà không có từ khóa mới thì bạn sẽ nhận được những gì bạn có ở đây, đó là cú pháp "ngôn ngữ cốt lõi" không có từ khóa mới và sử dụng các loại thư viện từ std.

Tôi nghĩ, điều tôi nghĩ ra là không có sự phân chia tuyệt đối trong C ++ giữa "ngôn ngữ cốt lõi" và các thư viện chuẩn. Chúng là các chương khác nhau trong tiêu chuẩn nhưng mỗi chương lại tham chiếu đến nhau, và nó luôn như vậy.

Có một cách tiếp cận khác trong C ++ 11, đó là lambdas giới thiệu các đối tượng có kiểu ẩn danh do trình biên dịch tạo ra. Bởi vì chúng không có tên nên chúng không ở trong một vùng tên nào cả, chắc chắn là không ở trong std. Tuy nhiên, đó không phải là một cách tiếp cận phù hợp cho danh sách trình khởi tạo, vì bạn sử dụng tên kiểu khi bạn viết hàm tạo chấp nhận một.


1
Đối với tôi, có vẻ như sự phân chia này là không thể (mailny?) Vì các vai trò ngầm định như vậy của các loại. type_infosize_tlà các đối số tốt .. cũng size_tchỉ là một lỗi đánh máy .. vì vậy chúng ta hãy bỏ qua điều này. Sự khác biệt giữa type_infoinitializer_listlà ở chỗ đầu tiên là kết quả của một toán tử rõ ràng và thứ hai là một hành động trình biên dịch ngầm định . Nó cũng có vẻ như đối với tôi, điều đó initializer_list có thể được thay thế bằng một số vùng chứa đã có sẵn .. hoặc tốt hơn: bất kỳ người dùng nào khai báo là loại đối số!
emesx

4
... hoặc đó có thể là lý do đơn giản là nếu bạn đã viết một phương thức khởi tạo vectornhận một hàm arraythì bạn có thể tạo một vectơ từ bất kỳ mảng nào có kiểu phù hợp, không chỉ một mảng được tạo bởi cú pháp danh sách trình khởi tạo. Tôi không chắc việc xây dựng các vùng chứa từ bất kỳ vùng chứa nào là một điều tồi tệ array, nhưng đó không phải là mục đích của ủy ban trong việc giới thiệu cú pháp mới.
Steve Jessop

2
@Christian: Không, std::arraythậm chí không có bất kỳ hàm tạo nào. Các std::arraytrường hợp chỉ đơn giản là tổng hợp-khởi. Ngoài ra, tôi hoan nghênh bạn tham gia với tôi trong phòng trò chuyện Lounge <C ++>, vì cuộc thảo luận này hơi dài.
Xeo

3
@ChristianRau: Xeo có nghĩa là các phần tử được sao chép khi danh sách trình khởi tạo được xây dựng. Sao chép danh sách trình khởi tạo không sao chép các phần tử được chứa.
Mooing Duck

2
@Christian Danh sách khởi tạo không ngụ ý khởi tạo danh sách. Nó có thể là nhiều thứ, bao gồm cả khởi tạo trực tiếp ole tốt hoặc khởi tạo tổng hợp. Không ai trong số đó liên quan đến danh sách khởi tạo (và một số không thể hoạt động theo cách đó).
R. Martinho Fernandes

42

Ủy ban Tiêu chuẩn C ++ dường như không muốn thêm các từ khóa mới, có lẽ vì điều đó làm tăng nguy cơ phá vỡ mã hiện có (mã kế thừa có thể sử dụng từ khóa đó làm tên của một biến, một lớp hoặc bất kỳ thứ gì khác).

Hơn nữa, đối với tôi, có vẻ như việc xác định std::initializer_listlà một vùng chứa được tạo khuôn mẫu là một lựa chọn khá thanh lịch: nếu đó là một từ khóa, bạn sẽ truy cập loại cơ bản của nó như thế nào? Bạn sẽ lặp lại nó như thế nào? Bạn cũng sẽ cần một loạt các toán tử mới và điều đó sẽ buộc bạn phải nhớ nhiều tên hơn và nhiều từ khóa hơn để thực hiện những điều tương tự bạn có thể làm với các vùng chứa tiêu chuẩn.

Đối xử với một vùng std::initializer_listchứa như bất kỳ vùng chứa nào khác mang lại cho bạn cơ hội viết mã chung hoạt động với bất kỳ thứ nào trong số đó.

CẬP NHẬT:

Sau đó, tại sao lại giới thiệu một kiểu mới, thay vì sử dụng một số kết hợp hiện có? (từ các bình luận)

Để bắt đầu, tất cả các vùng chứa khác đều có các phương thức để thêm, loại bỏ và thay thế các phần tử, điều này không mong muốn đối với bộ sưu tập do trình biên dịch tạo ra. Ngoại lệ duy nhất là std::array<>, bao bọc một mảng kiểu C có kích thước cố định và do đó sẽ vẫn là ứng cử viên hợp lý duy nhất.

Tuy nhiên, như Nicol Bolas đã chỉ ra một cách chính xác trong các nhận xét, một điểm khác biệt cơ bản khác giữa std::initializer_listvà tất cả các vùng chứa tiêu chuẩn khác (bao gồm cả std::array<>) là các vùng chứa sau có ngữ nghĩa giá trị , trong khi std::initializer_listngữ nghĩa tham chiếu . Sao chép một std::initializer_liství dụ, sẽ không tạo ra một bản sao của các phần tử mà nó chứa.

Hơn nữa (một lần nữa, xin phép của Nicol Bolas), có một vùng chứa đặc biệt cho danh sách khởi tạo dấu ngoặc nhọn cho phép quá tải trong cách người dùng thực hiện khởi tạo.


4
Sau đó, tại sao lại giới thiệu một kiểu mới, thay vì sử dụng một số kết hợp hiện có?
emesx

3
@elmes: Thực ra nó giống std::array . Nhưng std::arraycấp phát bộ nhớ trong khi std::initializaer_listkết thúc một mảng thời gian biên dịch. Hãy coi đó là sự khác biệt giữa char s[] = "array";char *s = "initializer_list";.
rodrigo

2
Và vì nó là một loại bình thường khiến cho quá tải, chuyên môn hóa mẫu, trang trí tên và những thứ tương tự, không thành vấn đề.
rodrigo

2
@rodrigo: std::arraykhông phân bổ bất kỳ bộ nhớ nào, đó là một thứ đơn giản T arr[N];, giống như thứ đang hỗ trợstd::initializer_list .
Xeo

6
@Xeo: T arr[N] có phân bổ bộ nhớ, có thể không phải trong đống động mà ở nơi khác ... Cũng vậy std::array. Tuy nhiên, không trốnginitializer_list không thể được người dùng xây dựng nên rõ ràng nó không thể cấp phát bộ nhớ.
rodrigo

6

Điều này không có gì mới. Ví dụ, for (i : some_container)dựa vào sự tồn tại của các phương thức cụ thể hoặc các hàm độc lập trong some_containerlớp. C # thậm chí còn dựa nhiều hơn vào các thư viện .NET của nó. Trên thực tế, tôi nghĩ rằng đây là một giải pháp khá tốt, bởi vì bạn có thể làm cho các lớp của mình tương thích với một số cấu trúc ngôn ngữ mà không làm phức tạp đặc tả ngôn ngữ.


2
các phương thức trong lớp hoặc độc lập beginendcác phương thức. IMO này hơi khác một chút.
emesx

3
Là nó? Một lần nữa, bạn có một cấu trúc ngôn ngữ thuần túy dựa trên cấu trúc cụ thể của mã của bạn. Nó cũng có thể đã được thực hiện bằng cách giới thiệu từ khóa mới, chẳng hạn,iterable class MyClass { };
Spook

nhưng bạn có thể đặt các phương thức ở bất cứ đâu bạn muốn, thực hiện chúng theo cách bạn muốn .. Có một số điểm tương đồng, tôi đồng ý! Câu hỏi này là về initializer_listmặc dù
emesx Ngày

4

Điều này thực sự không có gì mới và nhiều người đã chỉ ra, thực hành này đã có trong C ++ và ở đó, chẳng hạn, trong C #.

Tuy nhiên, Andrei Alexandrescu đã đề cập đến một điểm tốt về điều này: Bạn có thể nghĩ nó như một phần của không gian tên "lõi" tưởng tượng, sau đó nó sẽ có ý nghĩa hơn.

Vì vậy, nó thực sự là một cái gì đó như: core::initializer_list, core::size_t, core::begin(), core::end()và vân vân. Đây chỉ là một sự trùng hợp đáng tiếc khi stdkhông gian tên có một số cấu trúc ngôn ngữ cốt lõi bên trong nó.


2

Nó không chỉ có thể hoạt động hoàn toàn trong thư viện tiêu chuẩn. Việc đưa vào thư viện tiêu chuẩn không có nghĩa là trình biên dịch không thể chơi các thủ thuật thông minh.

Mặc dù có thể không thực hiện được trong mọi trường hợp, nhưng rất có thể nói rằng: loại này được nhiều người biết đến, hoặc loại đơn giản, chúng ta hãy bỏ qua initializer_list và chỉ có một hình ảnh bộ nhớ về giá trị được khởi tạo.

Nói cách khác int i {5};có thể tương đương với int i(5);hoặc int i=5;hoặc thậm chí intwrapper iw {5};Where intwrapperlà một lớp wrapper đơn giản trên một int với một hàm tạo tầm thường lấy mộtinitializer_list


Chúng ta có các ví dụ có thể tái tạo được về các trình biên dịch thực sự chơi "mánh khóe thông minh" như thế này không? Nó gần như là để lý luận dưới dạng như thể , nhưng tôi muốn xem chứng minh.
underscore_d

Ý tưởng của việc tối ưu hóa trình biên dịch là trình biên dịch có thể chuyển đổi mã thành bất kỳ mã nào tương đương. C ++ đặc biệt dựa vào việc tối ưu hóa cho các trừu tượng "miễn phí". Ý tưởng thay thế mã từ thư viện chuẩn là phổ biến (xem danh sách gcc nội trang gcc.gnu.org/onlineocs/gcc/Other-Builtins.html ).
Paul de Vrieze

Trên thực tế, ý tưởng của bạn int i {5}liên quan đến bất kỳ std::initializer_listlà sai. intkhông có hàm tạo std::initializer_list, vì vậy hàm 5chỉ được sử dụng trực tiếp để tạo nó. Vì vậy, ví dụ chính là không liên quan; đơn giản là không có tối ưu hóa nào được thực hiện. Ngoài ra, vì std::initializer_listliên quan đến việc trình biên dịch tạo và ủy quyền một mảng 'tưởng tượng', tôi đoán nó có thể giúp tối ưu hóa, nhưng đó là phần 'ma thuật' trong trình biên dịch, vì vậy nó tách biệt với việc liệu trình tối ưu hóa nói chung có thể làm bất cứ điều gì thông minh với cái đẹp đối tượng ngu si đần độn chứa 2 vòng lặp mà kết quả
underscore_d

1

Nó không phải là một phần của ngôn ngữ cốt lõi vì nó có thể được triển khai hoàn toàn trong thư viện, chỉ cần dòng operator newoperator delete. Sẽ có lợi thế gì khi làm cho các trình biên dịch phức tạp hơn để xây dựng nó?

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.