Tại sao các khái niệm (lập trình chung) được hình thành khi chúng ta đã có các lớp và giao diện?


8

Ngoài ra trên stackoverflow.com :

Tôi hiểu rằng các khái niệm STL phải tồn tại và sẽ thật ngớ ngẩn khi gọi chúng là "các lớp" hoặc "giao diện" khi thực tế chúng chỉ là các khái niệm tài liệu (con người) và không thể được dịch sang mã C ++ vào thời điểm đó, nhưng khi có cơ hội mở rộng ngôn ngữ để chứa các khái niệm, tại sao họ không đơn giản sửa đổi khả năng của các lớp và / hoặc giao diện được giới thiệu?

Không phải là một khái niệm rất giống với một giao diện (lớp trừu tượng 100% không có dữ liệu)? Nhìn vào nó, dường như các giao diện của tôi chỉ thiếu sự hỗ trợ cho các tiên đề, nhưng có lẽ các tiên đề có thể được đưa vào các giao diện của C ++ (xem xét việc áp dụng các giao diện giả định trong C ++ để tiếp quản các khái niệm), phải không? Tôi nghĩ ngay cả các khái niệm tự động cũng có thể dễ dàng được thêm vào giao diện C ++ như vậy (giao diện tự động LessThanComparable, có ai không?).

Không phải là một concept_map rất giống với mẫu Adaptor sao? Nếu tất cả các phương thức là nội tuyến, về cơ bản bộ điều hợp không tồn tại quá thời gian biên dịch; trình biên dịch chỉ cần thay thế các cuộc gọi đến giao diện bằng các phiên bản nội tuyến, gọi đối tượng đích trực tiếp trong thời gian chạy.

Tôi đã nghe nói về một thứ gọi là Lập trình hướng đối tượng tĩnh, về cơ bản có nghĩa là tái sử dụng hiệu quả các khái niệm hướng đối tượng trong lập trình chung, do đó cho phép sử dụng hầu hết sức mạnh của OOP mà không phải chịu chi phí thực hiện. Tại sao ý tưởng này không được xem xét thêm?

Tôi hy vọng điều này là đủ rõ ràng. Tôi có thể viết lại điều này nếu bạn nghĩ rằng tôi đã không làm vậy; hãy cho tôi biết.

Câu trả lời:


13

Câu trả lời ngắn: Bạn đang pha trộn các khái niệm trước thời gian biên dịchthời gian biên dịch có những điểm tương đồng trong mục đích của chúng. Các giao diện (các lớp trừu tượng và tất cả các triển khai mô hình hướng đối tượng) được tích hợp vào thời gian biên dịch . Các khái niệm là cùng một ý tưởng nhưng trong bối cảnh lập trình chung mà trong C ++ xảy ra TRƯỚC thời gian biên dịch . Chúng tôi chưa có tính năng cuối cùng này.

Nhưng hãy để tôi giải thích từ đầu.


Câu trả lời dài:

Trên thực tế, các khái niệm chỉ là thông tin ngôn ngữ và "dễ dàng hơn cho lập trình viên" về một thứ đã có trong ngôn ngữ, mà bạn có thể gọi là "gõ vịt".

Khi bạn chuyển một loại cho một hàm mẫu, đó là một hàm chung mà trình biên dịch sẽ tạo mã thực (nội tuyến) khi được gọi, loại đó cần phải có một số thuộc tính (đặc điểm?) Sẽ được sử dụng trong mã mẫu. Vì vậy, đó là ý tưởng gõ vịt NHƯNG tất cả được tạo ra và thực hiện vào thời gian biên dịch .

Điều gì xảy ra khi loại không có các thuộc tính cần thiết?

Vâng, trình biên dịch sẽ biết rằng chỉ có một vấn đề khi mã được tạo từ mẫu được biên dịch và bị lỗi. Điều đó có nghĩa là lỗi sẽ được tạo sẽ là một lỗi bên trong mã mẫu, lỗi này sẽ được hiển thị cho người lập trình là lỗi của anh ta. Ngoài ra, lỗi sẽ có hàng tấn thông tin do thông tin meta được cung cấp trong trường hợp tạo mã mẫu, để biết cách khởi tạo của mẫu mà chúng ta đang nói đến.

Một số vấn đề với điều đó: đầu tiên, hầu hết thời gian, mã mẫu là mã thư viện và hầu hết các lập trình viên là người sử dụng mã thư viện, không phải là người viết mã thư viện. Điều đó có nghĩa là loại lỗi khó hiểu này thực sự khó hiểu khi bạn không hiểu thư viện được viết như thế nào (không chỉ là thiết kế, cách nó được thực hiện). Vấn đề thứ hai là ngay cả khi lập trình viên đã viết mã mẫu, lý do thất bại vẫn có thể bị che khuất bởi vì trình biên dịch sẽ có thể nói rằng có một vấn đề quá muộn: khi mã được tạo đang được biên dịch. Nếu vấn đề liên quan đến các thuộc tính loại , thì nó nên kiểm tra ngay cả trước khi tạo mã.

Đó là những gì Khái niệm cho phép (và được thiết kế cho): cho phép lập trình viên (mã chung) chỉ định các thuộc tính của các loại được truyền dưới dạng tham số mẫu và sau đó cho phép trình biên dịch cung cấp các lỗi rõ ràng trong trường hợp các loại được cung cấp không đáp ứng yêu cầu.

Khi kiểm tra thành công, mã sẽ được tạo từ mẫu và sau đó được biên dịch, chắc chắn thành công.

Tất cả các kiểm tra Khái niệm xảy ra độc quyền trước thời gian biên dịch . Nó tự kiểm tra các loại, không phải các loại đối tượng . Không có đối tượng trước thời gian biên dịch.

Bây giờ, về "giao diện".

Khi bạn tạo một loại cơ sở trừu tượng hoặc ảo, bạn đang cho phép mã sử dụng nó để thao tác các đối tượng của các kiểu con mà không cần biết các triển khai thực sự của chúng. Để thực thi điều này, loại cơ sở phơi bày các thành viên ảo và có thể (hoặc phải) bị quá tải bởi các loại con.

Điều đó có nghĩa là trình biên dịch có thể kiểm tra tại thời điểm biên dịch rằng tất cả các đối tượng được truyền cho hàm yêu cầu tham chiếu đến lớp cơ sở phải 1. là một trong các kiểu con của lớp cơ sở, 2. kiểu con đó phải có các triển khai các hàm thuần ảo được khai báo trong các lớp cơ sở nếu có.

Vì vậy, tại thời gian biên dịch, trình biên dịch sẽ kiểm tra các giao diện của các loại đối tượng và báo cáo nếu thiếu một cái gì đó.

Đó là cùng một ý tưởng so với các khái niệm, nhưng nó xảy ra quá muộn , như đã nói trong phần mô tả Khái niệm. Nó xảy ra tại thời gian biên dịch. Chúng tôi không ở trong mã chung (mã mẫu), chúng tôi sau khi đã được xử lý và đã quá muộn để kiểm tra xem các loại có đáp ứng các yêu cầu chung hay không, có thể bị các lớp cơ sở ảo phơi bày. Trong thực tế, toàn bộ mô hình định hướng đối tượng trong C ++ thậm chí không tồn tại khi mã mẫu đang được xử lý. Không có đối tượng (chưa). Đó là

Các lớp mô tả các ràng buộc trên các đối tượng được sử dụng để kiểm tra các yêu cầu đối với các hàm thao tác với các đối tượng đó. Các khái niệm mô tả các ràng buộc về các loại (bao gồm các lớp) được sử dụng để kiểm tra các yêu cầu đối với mã chung để tạo mã thực từ các loại đó và kết hợp mã chung.

Vì vậy, một lần nữa, đó là "kiểm tra độ tỉnh" tương tự, nhưng trong một lớp khác của ngôn ngữ, đó là các mẫu. Mẫu là một ngôn ngữ đầy đủ (hoàn thành) cho phép lập trình meta, các loại lập trình ngay cả trước khi chúng xuất hiện trong mã được biên dịch. Nó hơi giống kịch bản trình biên dịch. Giả sử bạn có thể viết kịch bản, các lớp chỉ là các giá trị được thao tác bởi tập lệnh. Hiện tại, không có cách nào để kiểm tra các ràng buộc đối với các giá trị này ngoài việc phá vỡ tập lệnh theo cách không rõ ràng. Các khái niệm chỉ là: cung cấp nhập vào các giá trị này (mà trong mã được tạo là các loại). Không chắc là tôi rõ ...

Một sự khác biệt thực sự quan trọng khác giữa các lớp cơ sở ảo và các khái niệm là lớp thứ nhất buộc có mối quan hệ chặt chẽ giữa các loại, khiến chúng "bị ràng buộc bởi máu". Trong khi siêu lập trình mẫu cho phép "gõ vịt" mà các khái niệm chỉ cho phép làm cho các yêu cầu rõ ràng hơn.


4

Khai báo một lớp là " đối tượng lập trình ".
Khai báo một khái niệm là " các lớp lập trình ".

Tất nhiên, vì luôn luôn lập trình , nên có thể nhìn thấy một số sự tương tự, nhưng hai điều thuộc về một giai đoạn khác nhau của quá trình trừu tượng hóa. Về cơ bản một "lớp" (và mọi thứ xung quanh nó, như "giao diện") cho trình biên dịch biết cách cấu trúc các đối tượng mà máy thực thi sẽ khởi tạo khi chạy. Một "khái niệm" có nghĩa là nói cho trình biên dịch biết một "lớp" nên được cấu trúc như thế nào để "có thể biên dịch được" trong một ngữ cảnh cụ thể.

Tất nhiên, về mặt lý thuyết có thể nhắc lại các bước này nhiều lần

  • các đối tượng
  • loại đối tượng ( lớp )
  • loại (loại đối tượng) ( khái niệm )
  • loại (loại (loại đối tượng)) ( ??? )
  • .....

Ngay bây giờ, "các khái niệm" đã bị loại bỏ bởi các thông số kỹ thuật của C ++ 0x (vì chúng vẫn cần một số công việc và được giữ lại không phải là trường hợp để trì hoãn nữa) Ý tưởng về khái niệm n Tôi không biết - ngay bây giờ có thể trở nên hữu ích


Điều đó làm cho rất nhiều ý nghĩa về mặt khái niệm; Tôi đang cố gắng nhấn chìm nó và suy nghĩ nếu điều này trả lời câu hỏi. Cảm ơn rất nhiều.
Gui Prá

3

Câu trả lời đơn giản cho hầu hết tất cả các câu hỏi của bạn là "Bởi vì trình biên dịch C ++ hút". Nghiêm túc. Chúng được xây dựng trên công nghệ Đơn vị dịch thuật của C, có hiệu quả cấm nhiều thứ hữu ích và việc triển khai mẫu hiện có rất chậm. Các khái niệm không bị cắt vì bất kỳ lý do khái niệm nào - chúng bị cắt vì không có triển khai đáng tin cậy, ConceptGCC rất chậm và việc xác định các khái niệm mất một thời gian dài vô lý. Herb Sutter tuyên bố rằng cần nhiều không gian hơn để chỉ định các khái niệm được sử dụng trong thư viện Tiêu chuẩn hơn là chỉ định toàn bộ các mẫu.

Có thể cho rằng, giữa SFINAE decltypestatic_assert, hầu hết chúng đều có thể thực hiện được như bây giờ.


2

Theo hiểu biết của tôi, Giao diện và Khái niệm có mục đích tương tự trong các phần khác nhau của ngôn ngữ C ++.

Như đã đề cập trong phần trả lời cho câu hỏi ban đầu: Việc triển khai Giao diện được quyết định bởi người triển khai một lớp tại thời điểm thiết kế. Khi một lớp đã được xuất bản, nó chỉ có thể hỗ trợ các giao diện mà nó được bắt nguồn từ lúc thiết kế.

Hai Giao diện riêng biệt có cùng chức năng thành viên và ngữ nghĩa (nghĩa là cùng một khái niệm) vẫn sẽ là hai Giao diện riêng biệt. Nếu bạn muốn hỗ trợ ngữ nghĩa của cả hai Giao diện, bạn có thể phải thực hiện hỗ trợ hai lần.

Đó là vấn đề mà Lập trình chung dự định sẽ giải quyết. Trong lập trình chung C ++, loại được truyền cho một mẫu đơn giản chỉ cần hỗ trợ giao diện (không viết hoa, theo nghĩa "giao diện lập trình" của một loại) được sử dụng bởi mẫu. Hai Giao diện riêng biệt có cùng chức năng thành viên sẽ khớp và bạn chỉ cần viết mã một lần. Ngoài ra, mọi loại (thậm chí không có Giao diện rõ ràng) hỗ trợ cùng giao diện sẽ hoạt động với.

Điều này dẫn đến một vấn đề thứ hai: nếu bạn có hai loại với giao diện chồng chéo nhưng ngữ nghĩa khác nhau thì sao? Lập trình chung sẽ không thể cho biết sự khác biệt và mọi thứ sẽ được biên dịch tốt, nhưng kết quả thời gian chạy sẽ gây ngạc nhiên và có thể sai.

Đó là nơi các khái niệm xuất hiện. Nếu bạn quá đơn giản hóa, bạn có thể xem Khái niệm là phiên bản chung (mẫu) của Giao diện. Nó chỉ cần được triển khai một lần để áp dụng cho một số lượng lớn các loại tiềm năng có thể "xuất phát" từ Khái niệm. Khái niệm là một giao diện ngữ nghĩa được xác định trước của loại (lớp-) không chỉ giới hạn ở loại đó. Nó không giống như một Giao diện ở chỗ hai loại rất khác nhau (với trình biên dịch chung) vẫn có thể có cùng một Khái niệm, mà không phải dùng đến việc giới hạn các Giao diện lớp cơ sở hoặc Giao diện lớp cơ sở không có ngữ nghĩa thực sự của trình biên dịch riêng nhưng chỉ được sử dụng để phân biệt loại.


Đợi đã ... Bạn đã nói các khái niệm được triển khai một lần và áp dụng cho một số lượng lớn các loại tiềm năng có thể "rút ra" từ nó. Điều đó có nghĩa là tất cả các loại tiềm năng phải xuất phát từ nó. Nếu hai khái niệm có cùng nội dung xuất hiện trong hai thư viện khác nhau, không có ánh xạ tự động, bạn đã gặp vấn đề tương tự như Giao diện. Đồng thời, một Giao diện cũng có thể có tính năng ánh xạ tự động. Các vấn đề nhìn một và giống nhau với tôi.
Gui Prá

1
@ n2l Liquid - Các khái niệm là sự kết hợp khác nhau giữa lợi ích và nhược điểm giữa Giao diện và Lập trình Chung thuần túy. Họ không phải là một cải tiến nghiêm ngặt. Các khái niệm không tránh khỏi phần "được xác định trước" của Giao diện. Các khái niệm KHÔNG tránh tình huống các loại lớp hỗ trợ cùng một ngữ nghĩa nhưng không thể xuất phát từ cùng một Giao diện (ví dụ: giao diện được xác định cho kiểu con đôi, trong khi phiên bản chung áp dụng cho tất cả các loại số).
Joris Timmermans
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.