Sự khác biệt giữa C ++ 03 specifier C ++ 11 noexcept


100

Có sự khác biệt nào giữa throw()noexceptkhác với việc được kiểm tra trong thời gian chạy và thời gian biên dịch, tương ứng không?

Bài viết Wikipedia C ++ 11 này gợi ý rằng các chỉ định ném C ++ 03 không được dùng nữa.
Tại sao vậy, noexceptcó đủ khả năng để bao gồm tất cả những thứ đó tại thời điểm biên dịch không?

[Lưu ý: Tôi đã kiểm tra câu hỏi nàybài viết này , nhưng không thể xác định lý do chính xác cho việc không dùng nữa.]


7
Việc ghi chú vào bài viết hay này cũng noexceptcó thể phải kiểm tra thời gian chạy. Sự khác biệt chính giữa chúng là phá vỡ noexceptgây ra std::terminatetrong khi phá vỡ thrownguyên nhân std::unexpected. Cũng là một hành vi mở ngăn xếp hơi khác trong những trường hợp này.
Fiktik

Không có gì "thời gian biên dịch" được kiểm tra với một số thông số kỹ thuật ngoại lệ được kiểm tra "thời gian chạy" trên những người khác. Nó chỉ là một huyền thoại được tạo ra bởi các đối thủ của các đặc tả ngoại lệ C ++.
curiousguy

Câu trả lời:


129

Các mã chỉ định ngoại lệ không được dùng nữa vì các mã chỉ định ngoại lệ nói chung là một ý tưởng tồi . noexceptđã được thêm vào bởi vì đó là một cách sử dụng hợp lý hữu ích của bộ chỉ định ngoại lệ: biết khi nào một hàm không đưa ra ngoại lệ. Do đó, nó trở thành một lựa chọn nhị phân: các hàm sẽ ném và các hàm sẽ không ném.

noexcept đã được thêm vào thay vì chỉ xóa tất cả các chỉ số ném ngoài throw() bởi vì noexceptnó mạnh hơn. noexceptcó thể có một tham số mà thời gian biên dịch phân giải thành một boolean. Nếu boolean là true, thì đúng noexcept. Nếu boolean là false, thì noexceptkhông dính và hàm có thể ném.

Vì vậy, bạn có thể làm một cái gì đó như sau:

struct<typename T>
{
  void CreateOtherClass() { T t{}; }
};

Làm CreateOtherClass ném ngoại lệ không? Nó có thể, nếu hàm tạo Tmặc định của có thể. Làm thế nào để chúng tôi nói? Như thế này:

struct<typename T>
{
  void CreateOtherClass() noexcept(is_nothrow_default_constructible<T>::value) { T t{}; }
};

Do đó, CreateOtherClass()sẽ ném iff hàm tạo mặc định của kiểu đã cho. Điều này khắc phục một trong những vấn đề lớn với các chỉ định ngoại lệ: chúng không có khả năng truyền lên ngăn xếp cuộc gọi.

Bạn không thể làm điều này với throw().


+1 Câu trả lời hữu ích, cho tôi dù sao. Vẫn đang tìm kiếm câu trả lời cho biết lý do tại sao tôi muốn sử dụng noexcept. Tôi chưa bao giờ sử dụng throw()specifier, đã bao giờ và đang cố gắng xác định xem có noexceptthực sự cung cấp bất kỳ lợi ích nào không (ngoài tài liệu đã kiểm tra trình biên dịch).
hmjd 20/02/13

Chỉ cần thấy câu này stackoverflow.com/questions/10787766/... ...
hmjd

1
@NicolBolas đồng ý. nhưng nếu noexcept là một đảm bảo, thì trình biên dịch có thể kiểm tra xem một hàm có thể ném hay không trong một hàm hủy. Do đó, có thể cảnh báo một lập trình viên rằng một chức năng không được chấp nhận hay không.
Alex,

2
@NicolBắt đầu các cuộc gọi thời gian chạy std::terminate. đó là WAY WORSE ! mã có thể lẻn vào các bản phát hành có chức năng được đánh dấu noexcept và các vi phạm trong thời gian chạy (nghĩa là tại các trang web của khách hàng) được phát hiện. Ý tôi là trình biên dịch đảm bảo tạo ra mã không ném ngoại lệ ngay từ đầu.
Alex,

2
@NicolBolas: Một điểm khác biệt đáng chú ý. Nếu một hàm được đánh dấu throws()thì nếu một ngoại lệ được ném ra, ngăn xếp phải được mở ra khỏi phạm vi của hàm đó (vì vậy tất cả các biến tự động trong hàm đều bị hủy) tại thời điểm đó terminate()được gọi (qua unexpected()). Nếu một hàm được đánh dấu noexceptthì nếu một ngoại lệ được ném ra thì kết thúc sẽ được gọi (việc giải nén ngăn xếp là chi tiết được xác định thực thi).
Martin York

33

noexcept không được kiểm tra tại thời điểm biên dịch.

Một triển khai sẽ không từ chối một biểu thức chỉ vì khi được thực thi, nó ném hoặc có thể ném một ngoại lệ mà hàm chứa không cho phép.

Khi một hàm được khai báo noexcepthoặc throw()cố gắng ném một ngoại lệ, điểm khác biệt duy nhất là một lệnh gọi terminatevà lệnh gọi unexpectedvà kiểu xử lý ngoại lệ sau này đã không còn được chấp nhận.


Nhưng nếu một hàm ảo có throw()/ noexcept, kiểm tra thời gian biên dịch đảm bảo rằng một trình ghi đè cũng có.
curiousguy

2

std::unexpected() được gọi bởi thời gian chạy C ++ khi một đặc tả ngoại lệ động bị vi phạm: một ngoại lệ được ném ra từ một hàm có đặc tả ngoại lệ cấm các ngoại lệ thuộc loại này.

std::unexpected() cũng có thể được gọi trực tiếp từ chương trình.

Trong cả hai trường hợp, std::unexpectedgọi là cài đặt hiện tại std::unexpected_handler. Các std::unexpected_handlercuộc gọi mặc định std::terminate.

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.