Những thông tin dưới đây đã lỗi thời. Nó cần được cập nhật theo bản nháp Con Concept Lite mới nhất.
Phần 3 của đề xuất ràng buộc đề cập đến vấn đề này một cách hợp lý.
Đề xuất khái niệm đã được đặt ra trong một thời gian ngắn với hy vọng rằng các ràng buộc (tức là khái niệm-lite) có thể được bổ sung và thực hiện trong một quy mô thời gian ngắn hơn, hiện đang hướng tới mục tiêu ít nhất là trong C ++ 14. Đề xuất ràng buộc được thiết kế để hoạt động như một quá trình chuyển đổi suôn sẻ sang định nghĩa khái niệm sau này. Ràng buộc là một phần của đề xuất khái niệm và là một khối xây dựng cần thiết trong định nghĩa của nó.
Trong Thiết kế Thư viện Khái niệm cho C ++ , Sutton và Stroustrup xem xét mối quan hệ sau:
Khái niệm = Ràng buộc + Tiên đề
Để nhanh chóng tóm tắt ý nghĩa của chúng:
- Ràng buộc - Một vị từ trên các thuộc tính có giá trị được đánh giá tĩnh của một kiểu. Yêu cầu hoàn toàn về cú pháp. Không phải là một miền trừu tượng.
- Tiên đề - Yêu cầu về ngữ nghĩa của các kiểu được cho là đúng. Không được kiểm tra tĩnh.
- Khái niệm - Yêu cầu chung, trừu tượng của thuật toán đối với đối số của chúng. Định nghĩa trong điều kiện ràng buộc và tiên đề.
Vì vậy, nếu bạn thêm các tiên đề (thuộc tính ngữ nghĩa) vào các ràng buộc (thuộc tính cú pháp), bạn sẽ có được các khái niệm.
Khái niệm-Lite
Đề xuất khái niệm-lite chỉ mang đến cho chúng ta phần đầu tiên, những ràng buộc, nhưng đây là một bước quan trọng và cần thiết để hướng tới các khái niệm chính thức.
Ràng buộc
Các ràng buộc đều là về cú pháp . Chúng cung cấp cho chúng ta một cách xác định tĩnh thuộc tính của một kiểu tại thời điểm biên dịch, để chúng ta có thể hạn chế các kiểu được sử dụng làm đối số mẫu dựa trên các thuộc tính cú pháp của chúng. Trong đề xuất hiện tại cho các ràng buộc, chúng được thể hiện bằng một tập con của phép tính mệnh đề bằng cách sử dụng các liên kết logic như &&
và ||
.
Hãy xem xét một hạn chế trong hoạt động:
template <typename Cont>
requires Sortable<Cont>()
void sort(Cont& container);
Ở đây chúng ta được xác định mẫu hàm được gọi sort
. Bổ sung mới là mệnh đề yêu cầu . Mệnh đề yêu cầu đưa ra một số ràng buộc đối với các đối số mẫu cho hàm này. Đặc biệt, ràng buộc này nói rằng kiểu Cont
phải là một Sortable
kiểu. Một điều gọn gàng là nó có thể được viết dưới dạng ngắn gọn hơn như:
template <Sortable Cont>
void sort(Cont& container);
Bây giờ nếu bạn cố gắng chuyển bất cứ thứ gì không được coi là Sortable
cho hàm này, bạn sẽ gặp một lỗi rất hay ngay lập tức cho bạn biết rằng kiểu được suy ra T
không phải là Sortable
kiểu. Nếu bạn đã làm điều này trong C ++ 11, bạn sẽ có một số lỗi khủng khiếp ném từ bên trong các sort
chức năng mà làm cho không có ý nghĩa cho bất kỳ ai.
Các vị từ ràng buộc rất giống với các đặc điểm kiểu. Họ lấy một số kiểu đối số mẫu và cung cấp cho bạn một số thông tin về nó. Ràng buộc cố gắng trả lời các loại câu hỏi sau về loại:
- Loại này có toán tử như vậy bị quá tải không?
- Các kiểu này có thể được sử dụng làm toán hạng cho toán tử này không?
- Loại này có đặc điểm như vậy không?
- Biểu thức hằng này có bằng không? (đối với các đối số mẫu không phải kiểu)
- Loại này có một hàm gọi là yada-yada trả về loại đó không?
- Loại này có đáp ứng tất cả các yêu cầu cú pháp để được sử dụng như vậy không?
Tuy nhiên, các ràng buộc không có nghĩa là để thay thế các đặc điểm kiểu. Thay vào đó, họ sẽ làm việc tay đôi. Một số tính trạng kiểu hiện nay có thể được định nghĩa bằng khái niệm và một số khái niệm dưới dạng tính trạng kiểu.
Ví dụ
Vì vậy, điều quan trọng về các ràng buộc là họ không quan tâm đến ngữ nghĩa một iota. Một số ví dụ điển hình về các ràng buộc là:
Equality_comparable<T>
: Kiểm tra xem kiểu có ==
với cả hai toán hạng cùng kiểu hay không.
Equality_comparable<T,U>
: Kiểm tra xem có một ==
với toán hạng trái và phải của các loại đã cho
Arithmetic<T>
: Kiểm tra xem kiểu có phải là kiểu số học hay không.
Floating_point<T>
: Kiểm tra xem kiểu có phải là kiểu dấu phẩy động hay không.
Input_iterator<T>
: Kiểm tra xem kiểu có hỗ trợ các hoạt động cú pháp mà trình lặp đầu vào phải hỗ trợ hay không.
Same<T,U>
: Kiểm tra xem loại đã cho có giống nhau không.
Bạn có thể thử tất cả những điều này với một bản dựng khái niệm đặc biệt của GCC .
Vượt ra ngoài khái niệm-Lite
Bây giờ chúng ta đi sâu vào mọi thứ ngoài đề xuất khái niệm-Lite. Điều này thậm chí còn tương lai hơn chính tương lai. Mọi thứ từ đây trở đi có thể sẽ thay đổi khá nhiều.
Tiên đề
Tiên đề là tất cả về ngữ nghĩa . Chúng chỉ định các mối quan hệ, bất biến, đảm bảo phức tạp và những thứ khác. Hãy xem một ví dụ.
Mặc dù Equality_comparable<T,U>
ràng buộc sẽ cho bạn biết rằng có một operator==
loại có các loại T
và U
, nó không cho bạn biết hoạt động đó có nghĩa là gì . Đối với điều đó, chúng ta sẽ có tiên đề Equivalence_relation
. Tiên đề này nói rằng khi các đối tượng của hai loại này được so sánh với operator==
việc cho true
, các đối tượng này là tương đương. Điều này có vẻ thừa, nhưng chắc chắn là không. Bạn có thể dễ dàng xác định một operator==
mà thay vào đó hoạt động như một operator<
. Bạn sẽ thật ác khi làm điều đó, nhưng bạn có thể.
Một ví dụ khác là một Greater
tiên đề. Thật tốt khi nói hai đối tượng kiểu T
có thể được so sánh với >
và <
toán tử, nhưng chúng có nghĩa là gì? Các Greater
tiên đề nói rằng khi và chỉ khi x
lớn rồi y
, sau đó y
là ít hơn x
. Đặc điểm kỹ thuật được đề xuất một tiên đề như vậy trông giống như:
template<typename T>
axiom Greater(T x, T y) {
(x>y) == (y<x);
}
Vì vậy, tiên đề trả lời các loại câu hỏi sau:
- Hai toán tử này có mối quan hệ này với nhau không?
- Toán tử này cho loại như vậy có nghĩa là điều này không?
- Thao tác trên kiểu đó có phức tạp không?
- Kết quả này của toán tử có ngụ ý rằng điều này là đúng không?
Có nghĩa là, họ hoàn toàn quan tâm đến ngữ nghĩa của các kiểu và hoạt động trên các kiểu đó. Những thứ này không thể được kiểm tra tĩnh. Nếu điều này cần được kiểm tra, một kiểu nào đó phải tuyên bố rằng nó tuân theo những ngữ nghĩa này.
Ví dụ
Dưới đây là một số ví dụ phổ biến về tiên đề:
Equivalence_relation
: Nếu hai đối tượng so sánh ==
, chúng là tương đương.
Greater
: Bất cứ khi nào x > y
, sau đó y < x
.
Less_equal
: Bất cứ khi nào x <= y
, sau đó !(y < x)
.
Copy_equality
: Đối với x
và y
của loại T
: nếu x == y
, một đối tượng mới cùng loại được tạo ra bằng cách xây dựng bản sao T{x} == y
và vẫn x == y
(nghĩa là nó không phá hủy).
Các khái niệm
Bây giờ các khái niệm rất dễ định nghĩa; chúng chỉ đơn giản là sự kết hợp của các ràng buộc và tiên đề . Chúng cung cấp một yêu cầu trừu tượng về cú pháp và ngữ nghĩa của một kiểu.
Ví dụ, hãy xem xét Ordered
khái niệm sau :
concept Ordered<Regular T> {
requires constraint Less<T>;
requires axiom Strict_total_order<less<T>, T>;
requires axiom Greater<T>;
requires axiom Less_equal<T>;
requires axiom Greater_equal<T>;
}
Lưu ý đầu tiên cho kiểu mẫu T
được Ordered
, nó cũng phải đáp ứng các yêu cầu của Regular
khái niệm. Các Regular
khái niệm này là một yêu cầu rất cơ bản mà các loại là well-behaved - nó có thể được xây dựng, phá hủy, sao chép và so sánh.
Ngoài các yêu cầu đó, các Ordered
yêu cầu T
đáp ứng một ràng buộc và bốn tiên đề:
- Ràng buộc: Một
Ordered
kiểu phải có operator<
. Điều này được kiểm tra tĩnh nên nó phải tồn tại.
- Tiên đề: Đối
x
và y
loại T
:
x < y
đưa ra một thứ tự tổng số nghiêm ngặt.
- Khi nào
x
lớn hơn y
, y
nhỏ hơn x
, và ngược lại.
- Khi
x
nhỏ hơn hoặc bằng y
, y
không nhỏ hơn x
, và ngược lại.
- Khi
x
lớn hơn hoặc bằng y
, y
không lớn hơn x
, và ngược lại.
Kết hợp các ràng buộc và tiên đề như thế này cho bạn các khái niệm. Chúng xác định các yêu cầu về cú pháp và ngữ nghĩa cho các kiểu trừu tượng để sử dụng với các thuật toán. Các thuật toán hiện phải giả định rằng các kiểu được sử dụng sẽ hỗ trợ các hoạt động nhất định và thể hiện ngữ nghĩa nhất định. Với các khái niệm, chúng tôi sẽ có thể đảm bảo rằng các yêu cầu được đáp ứng.
Trong thiết kế khái niệm mới nhất , trình biên dịch sẽ chỉ kiểm tra xem các yêu cầu cú pháp của khái niệm có được đáp ứng bởi đối số mẫu hay không. Các tiên đề được bỏ chọn. Vì tiên đề biểu thị ngữ nghĩa không có giá trị tĩnh (hoặc thường không thể kiểm tra hoàn toàn), tác giả của một kiểu sẽ phải tuyên bố rõ ràng rằng kiểu của chúng đáp ứng tất cả các yêu cầu của một khái niệm. Điều này được gọi là bản đồ khái niệm trong các thiết kế trước đây nhưng sau đó đã bị loại bỏ.
Ví dụ
Dưới đây là một số ví dụ về các khái niệm:
Regular
các kiểu có thể xây dựng, có thể hủy, có thể sao chép và có thể được so sánh.
Ordered
hỗ trợ các loại operator<
và có tổng thứ tự nghiêm ngặt và ngữ nghĩa sắp xếp khác.
Copyable
các kiểu là bản sao có thể xây dựng, có thể hủy, và nếu x
bằng y
và x
được sao chép, bản sao cũng sẽ so sánh bằng y
.
Iterator
loại loại phải gắn liền value_type
, reference
, difference_type
, và iterator_category
đó chính họ phải đáp ứng một số khái niệm. Họ cũng phải hỗ trợ operator++
và có thể tham khảo.
Con đường dẫn đến khái niệm
Ràng buộc là bước đầu tiên hướng tới một tính năng khái niệm đầy đủ của C ++. Chúng là một bước rất quan trọng, vì chúng cung cấp các yêu cầu có thể thực thi tĩnh của các loại để chúng ta có thể viết các hàm và lớp mẫu sạch hơn nhiều. Bây giờ chúng ta có thể tránh một số khó khăn và xấu xí của std::enable_if
và những người bạn lập trình siêu hình của nó.
Tuy nhiên, có một số điều mà đề xuất ràng buộc không thực hiện được:
Nó không cung cấp một ngôn ngữ định nghĩa khái niệm.
Ràng buộc không phải là bản đồ khái niệm. Người dùng không cần phải chú thích cụ thể các loại của họ khi đáp ứng các ràng buộc nhất định. Chúng được kiểm tra tĩnh đã sử dụng các tính năng ngôn ngữ thời gian biên dịch đơn giản.
Việc triển khai các mẫu không bị ràng buộc bởi các ràng buộc trên các đối số mẫu của chúng. Nghĩa là, nếu mẫu hàm của bạn thực hiện bất kỳ điều gì với một đối tượng có kiểu ràng buộc mà nó không nên làm, trình biên dịch không có cách nào để chẩn đoán điều đó. Một đề xuất khái niệm đầy đủ tính năng sẽ có thể làm được điều này.
Đề xuất ràng buộc đã được thiết kế đặc biệt để đề xuất khái niệm đầy đủ có thể được giới thiệu trên đó. Với bất kỳ may mắn nào, quá trình chuyển đổi đó sẽ là một chuyến đi khá suôn sẻ. Nhóm khái niệm đang tìm cách giới thiệu các ràng buộc cho C ++ 14 (hoặc trong một báo cáo kỹ thuật ngay sau đó), trong khi các khái niệm đầy đủ có thể bắt đầu xuất hiện vào khoảng thời gian C ++ 17.