[英]Does the C++ standard mandate that C-linkage functions are `noexcept`?
我在標准中找不到任何強制使用extern "C"
聲明的函數為noexcept
,無論是隱式還是顯式。
然而,應該清楚的是,C 調用約定不能支持異常……或者是嗎?
標准是否提到了這一點,我錯過了某個地方? 如果沒有,為什么不呢? 它是否只是作為各種實現細節留下?
據我所知,不能保證用“C”鏈接定義的函數不會拋出異常。 該標准允許 C++ 程序調用具有“C”語言鏈接的外部函數,以及定義用 C++ 編寫的具有“C”語言鏈接的函數。 因此,沒有什么可以阻止 C++ 程序調用具有“C”語言鏈接的函數,而該函數實際上是用 C++ 編寫的(可能在另一個編譯單元中,盡管這並不是必需的)。 這樣做會很奇怪,但很難排除。 我也沒有看到標准中的哪里說這樣做會導致未定義的行為(事實上,由於標准無法定義不是用 C++ 編寫的函數的行為,這將是沒有正式未定義行為的唯一用法)。
因此,我認為假設“C”鏈接意味着noexcept
是錯誤的。
嗯,我假設extern "C"
只使用 C-linkage,而不是 C 函數。 它可以防止編譯器執行C++ 名稱修改。
更直接 - 假設這段代碼。
// foo.cpp
extern "C" void foo()
{
throw 1;
}
// bar.cpp
extern "C" void foo();
void bar()
{
try
{
foo();
}
catch (int)
{
// yeah!
}
}
Marc van Leeuwen 的回答是正確的:查找當前的工作草案,似乎沒有任何內容要求聲明extern "C"
函數是隱式noexcept
。 有趣的是,標准 C++ 禁止 C++ 標准庫中的 C 標准庫函數拋出異常。 這些函數本身通常被指定為extern "C"
(但這是定義的實現,參見16.4.3.3-2 )。 查看第16.4.6.13 條[對異常處理的限制] 以及隨附的腳注174和175 。
C 標准庫中的函數不應拋出異常 [腳注 174],除非此類函數調用程序提供的拋出異常的函數。 [腳注 175]
腳注 174:
- 也就是說,C 庫函數都可以被視為標記為 noexcept。 這允許實現基於運行時不存在異常進行性能優化。
腳注 175:
函數 qsort() 和 bsearch() ([alg.c.library]) 滿足這個條件。
話雖如此,遵循與標准庫相同的策略通常是一個很好的設計指南,出於Marc van Leeuwen 的回答中提到的原因,我認為用戶定義的extern "C"
函數也用noexcept
指定是個好主意,除非它傳遞一個指向 C++ 函數的指針作為回調參數,如 qsort 等。 我用下面的代碼用clang10、gcc10做了一個小實驗:
#include <cstring>
#include <cstdlib>
#include <iostream>
extern "C" int cmp(const void* lhs, const void* rhs) noexcept;
extern "C" int non_throwing();
int main()
{
constexpr int src[] = {10, 9, 8, 7, 6, 5};
constexpr auto sz = sizeof *src;
constexpr auto count = sizeof src / sz;
int dest[count];
int key = 7;
std::cout << std::boolalpha
// noexcept is unevaluated so no worries about UB here
<< "non_throwing: " << noexcept(non_throwing()) << '\n'
<< "memcpy: " << noexcept(std::memcpy(dest, src, sizeof dest)) << '\n'
<< "malloc: "<< noexcept(std::malloc(16u)) << '\n'
<< "free: " << noexcept(std::free(dest)) << '\n'
<< "exit: " << noexcept(std::exit(0)) << '\n'
<< "atexit: " << noexcept(std::atexit(nullptr)) << '\n'
<< "qsort: " << noexcept(std::qsort(dest, count, sz, cmp)) << '\n' // should fail
<< "bsearch: " << noexcept(std::bsearch(&key, dest, count, sz, cmp)) << '\n'; // should fail
}
gcc10 和 clang10 的輸出是:
non_throwing: false
memcpy: true
malloc: true
free: true
exit: true
atexit: true
qsort: false
bsearch: false
對於 msvc142,如果使用 /EHsc 編譯,則所有輸出顯然都是true
。 使用 /EHs,所有輸出都是假的,這使得 /EHsc 中的“c”必須嚴格遵守。
沒有任何地方說extern "C"
函數是noexcept
。 另一方面,幾乎所有的 C 標准庫函數都是noexcept
除非你做了一些奇怪的事情。 通常,這歸結為調用未定義的行為,但還有一些其他情況。 這些應該是全部:
qsort()
的函數指針參數可以拋出; 因此qsort()
可以拋出。bsearch()
也是如此。malloc()
、 realloc()
和free()
。 如果你這樣做,這些可能會拋出。calloc()
、 fopen()
、 fclose()
、 freopen()
、 system()
和strdup()
也可能拋出。 ( strdup()
已定義但不保證存在。)setjmp()
和catch(...)
不要混用。 至少有一個平台將longjmp()
實現為throw jmp_buf
的邏輯等價物,從而導致catch(...)
捕獲它。catch(...)
。 如果您在任何地方執行未定義行為,則一旦代碼路徑不可撤銷地致力於達到未定義行為,整個程序就會未定義,因此這可能導致 C 標准庫函數拋出。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.