noexcept 是 C++11 中引入的一個關鍵字,用來標記函數聲明,表示該函數不會拋出異常。它可以用于函數、函數指針、Lambda 表達式等。使用 noexcept 可以幫助編譯器進行優化,提高代碼的執行效率,并且讓程序在處理異常時更加明確。
1. 基本語法
void foo() noexcept {// 這個函數不會拋出異常
}void bar() {// 這個函數可能會拋出異常
}
2. 作用
- 提高性能:編譯器知道一個函數不會拋出異常后,可以進行更多的優化(例如:不需要為異常處理創建額外的代碼路徑)。
- 異常安全性:顯式地聲明一個函數不會拋出異常,可以使代碼的異常行為更清晰,增強代碼的可維護性。
- 條件約束:可以用 noexcept 來限制函數的使用。例如,標準庫中的 std::vector::swap 如果是 noexcept,那么它可以在 std::vector 中進行更多的優化。
3. 如何使用
標記函數:
使用 noexcept 修飾符來指示函數不拋出異常:
void foo() noexcept {// 函數實現
}
推斷函數是否 noexcept:
C++11 中還引入了 noexcept 運算符,用來推斷某個函數是否會拋出異常。
template<typename T>
void func(T&& arg) noexcept(noexcept(T(std::forward<T>(arg)))) {// 根據類型 T 的構造函數是否 noexcept 來決定是否 noexcept
}
4. noexcept 運算符
C++11 引入了 noexcept 運算符,可以用來檢查某個表達式或類型是否會拋出異常:
template <typename T>
void check_if_noexcept() {if constexpr (noexcept(T())) {std::cout << "T() is noexcept\n";} else {std::cout << "T() is not noexcept\n";}
}
5. noexcept 與異常的關系
- 如果你聲明一個函數為 noexcept,但該函數拋出了異常,程序將調用 std::terminate(),從而導致程序終止。這是因為 noexcept 函數承諾不會拋出異常,違背這一承諾會導致程序的未定義行為。
- 函數的 noexcept 狀態可以通過以下規則推斷:
- 如果函數體內包含任何可能拋出異常的操作,則該函數不應標記為 noexcept。
- 如果函數內部明確聲明不拋出異常,例如函數內部只調用其他 noexcept 函數,則該函數可以標記為 noexcept。
6. 示例代碼
#include <iostream>void foo() noexcept {std::cout << "foo is noexcept\n";
}void bar() {throw std::runtime_error("bar throws exception");
}int main() {foo(); // No exception, works finetry {bar(); // Throws exception, catch it} catch (const std::exception& e) {std::cout << e.what() << std::endl;}return 0;
}
7. noexcept 和 Lambda 表達式
C++11 允許你在 Lambda 表達式中使用 noexcept:
auto lambda = []() noexcept { std::cout << "This is noexcept lambda\n"; };
lambda();
8. 注意事項
- 默認情況下,函數不拋出異常:在 C++11 之前,函數沒有明確標記為 noexcept,默認認為它可能會拋出異常。
- 函數指針與 noexcept:如果一個函數指針指向的函數被標記為 noexcept,你也需要明確聲明函數指針為 noexcept:
void (*fp)() noexcept = foo;
總結
- noexcept 是 C++11 提供的一個關鍵字,標記函數不會拋出異常。
- 它可以幫助編譯器進行性能優化,并增強代碼的可維護性。
- 使用時需要注意不要在可能拋出異常的函數中標記為 noexcept,否則會導致程序終止。