我在标准中找不到任何强制声明为的函数的内容extern "C"
,noexcept
无论是隐式还是显式。
然而,应该清楚的是 C 调用约定不能支持异常......或者是吗?
标准是否在我错过的地方提到了这一点?如果不是,为什么不呢?它只是作为某种实现细节而留下吗?
我在标准中找不到任何强制声明为的函数的内容extern "C"
,noexcept
无论是隐式还是显式。
然而,应该清楚的是 C 调用约定不能支持异常......或者是吗?
标准是否在我错过的地方提到了这一点?如果不是,为什么不呢?它只是作为某种实现细节而留下吗?
据我所知,不能保证用“C”链接定义的函数不会抛出异常。该标准允许 C++ 程序调用具有“C”语言链接的外部函数,并定义用 C++ 编写的具有“C”语言链接的函数。因此,没有什么可以阻止 C++ 程序调用具有“C”语言链接的函数,该函数实际上是用 C++ 编写的(可能在另一个编译单元中,尽管这不是必需的)。这将是一件奇怪的事情,但很难排除。此外,我没有看到标准中的哪个地方说这样做会导致未定义的行为(事实上,由于标准无法定义未用 C++ 编写的函数的行为,
因此,我认为假设“C”链接意味着noexcept
.
嗯,我假设extern "C"
只使用 C 链接,而不是 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(...)
一些系统实际上确实将 *NULL 实现为抛出异常,即使在编译 C 代码时也可以捕获该异常。如果您在任何地方执行未定义的行为,一旦代码路径不可撤销地致力于达到未定义的行为,整个程序就未定义,因此这可能会导致 C 标准库函数抛出异常。