Tôi đang thêm câu trả lời ở đây để làm rõ sự khác biệt giữa hành vi không đúng định dạng và không xác định.
[intro.compliance]/p1:
Các bộ quy tắc có thể chẩn đoán bao gồm tất cả các cú pháp và ngữ nghĩa quy tắc trong tiêu chuẩn quốc tế này, ngoại trừ đối với những quy tắc chứa một ký hiệu rõ ràng rằng “không chẩn đoán được yêu cầu ”hoặc là mô tả là dẫn đến‘hành vi không xác định’
[defns.ill.formed]:.
chương trình mà không được cũng được hình thành
[defns.well.formed]
chương trình
C++ được xây dựng theo các quy tắc cú pháp, có thể chẩn đoán quy tắc ngữ nghĩa, và One Definition Rule (3.2) .
Bằng tiếng Anh: chương trình không đúng định dạng sẽ có chẩn đoán liên quan đến nó. Hành vi không xác định có thể làm bất cứ điều gì:
- Nó có thể biên dịch và thực hiện như bạn dự định.
- Nó có thể đưa ra chẩn đoán.
- Nó có thể xóa mã bạn đã viết.
- Nó có thể định dạng lại đĩa gần nhất.
(tất cả nhưng thứ 4 thường xuyên xảy ra trong thực tế)
hành vi Không xác định là rất xấu, và IMHO, C và C++ tiêu chuẩn áp dụng đặc điểm kỹ thuật mà nhiều quá tự do.
Về mặt kỹ thuật, vi phạm một mệnh đề Yêu cầu dẫn đến hành vi không xác định.
[res.on.required]/p1:
Vi phạm các điều kiện tiên quyết quy định trong một chức năng của Yêu cầu: kết quả đoạn trong hành vi undefined trừ của chức năng Ném: đoạn quy định cụ thể ném một ngoại lệ khi điều kiện tiên quyết bị vi phạm .
Theo ghi nhận của MSN, allocator_type::value_type
sẽ được giống như container::value_type
như đã nêu trong Bảng 99 - cấp phát-aware chứa yêu cầu.
allocator_type A Requires: allocator_type::value_type
is the same as X::value_type.
(X
biểu thị một lớp container cấp phát-aware với một value_type
của T
sử dụng cấp phát loại A
)
Vì vậy, một sự vi phạm như:
std::list<int, std::allocator<long> >
là hành vi không xác định. Vì vậy, nó:
- có thể biên dịch và thực hiện như bạn dự định.
- có thể đưa ra chẩn đoán.
- có thể xóa mã bạn đã viết.
- có thể định dạng lại đĩa gần nhất.
Chỉ rất gần đây (trong vòng tuần tôi viết thư này), libC++ (http://libcxx.llvm.org) đã bắt đầu chẩn đoán xác định hành vi này với static_assert
để bạn có được những tin tức xấu càng sớm càng tốt.
Chúng tôi quyết định đi theo hướng này, thay vì cho phép hành vi vì các vùng chứa không được thiết lập để cho phép chuyển đổi giữa các loại có liên quan chặt chẽ. Ví dụ:
std::list<int, std::allocator<long>> list1;
std::list<int> list2 = list1; // is specified to not work
I.e. nếu bạn bắt đầu xử lý list1
và list2
là các loại tương đương vì std::allocator
nhận được rebind
'd anyway, bạn sẽ thấy thất vọng khi bạn khám phá hai danh sách thực sự là các loại khác nhau và không được thiết kế để tương thích. Vì vậy, nó thực sự là tốt nhất để có được những tin tức xấu càng sớm càng tốt, thay vì tìm ra 2 tháng, hoặc 2 năm sau đó, khi bạn cố gắng sử dụng chúng như các loại tương đương.
Có thể tiêu chuẩn tương lai sẽ xử lý các loại tương đương list1
và list2
. Đó là chủ yếu về mặt kỹ thuật có thể (std::is_same
có thể sẽ không hoạt động). Nhưng không có đề xuất nào mà tôi đã nghe theo hướng này. Hướng này dường như không có khả năng đối với tôi. Và với static_assert
, lỗi có thể dễ dàng chẩn đoán được.Thay vào đó, tôi muốn thấy việc di chuyển tiêu chuẩn theo hướng làm cho mã này không đúng định dạng thay vì không xác định. Phần khó nhất trong việc làm như vậy sẽ là từ-smithing tiêu chuẩn, và không phải trong std :: lib thực hiện.
kiểm tra kích thước của cấu trúc dữ liệu khi bạn sử dụng các loại khác nhau – aaronman
@aaronman: Kích thước của 'std :: list' có thể hoặc không khác nhau khi các loại dữ liệu khác nhau được sử dụng cho loại hoặc cấp phát. Điều đó sẽ không thực sự cho bạn biết bất cứ điều gì –
@ DavidRodríguez-dribeas trong trường hợp danh sách bạn sẽ kiểm tra kích thước của một nút nếu điều đó thậm chí có thể là – aaronman