我在标准中没有找到强制使用 extern "C"
声明的函数必须是 noexcept
的任何内容,无论是隐式还是显式。
然而,显然,C 调用约定无法支持异常...或者说不行吗?
标准是否提及了这一点,是否有我错过的地方?如果没有,为什么呢?它仅仅被留作某种实现细节吗?
我在标准中没有找到强制使用 extern "C"
声明的函数必须是 noexcept
的任何内容,无论是隐式还是显式。
然而,显然,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!
}
}
extern "C++" void Foo();
与extern "C" void Foo();
是不同的类型,具有不同的属性,其中链接性只是其中之一。 - Captain ObvliousMarc van Leeuwen的回答是正确的:查找当前工作草案,没有强制要求声明为extern "C"
的函数隐式为noexcept
。有趣的是,标准C ++禁止在C ++标准库中抛出C标准库函数。这些函数通常被指定为extern "C"
(但这是实现定义,请参见16.4.3.3-2)。请查看条款16.4.6.13 [对异常处理的限制]以及附带的脚注174和175。
#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,则所有输出都为false,这使得在严格符合性方面需要/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代码时也是如此。如果您在任何地方执行未定义行为,则整个程序在代码路径无法撤销地致力于达到未定义行为时就变成了未定义行为,因此这可能会导致C标准库函数抛出异常。qsort
或 bsearch
的比较函数抛出异常,行为是否已经定义?一些平台的异常处理机制只有当 throw
和 catch
之间的每个堆栈帧具有特定布局时才能工作,但在不需要堆栈展开时,其他布局可能更有效率。对于这样的平台,C编译器通常没有理由不使用这样的布局。 - supercatfopen()
造成麻烦,但我无法想象对于qsort()
会有什么问题。此外,我期望在一个不支持此功能的架构上,从C++中包含qsort()
的函数指针将被声明为noexcep
。他们已经不得不使用#ifdef
来使链接工作,所以这不是太麻烦。 - Joshuarand()
)之外使用静态存储,其语义自然需要它。 - supercat
noexcept
来正式假定给定的C函数在处理C++异常方面表现正确?”,就像我们期望汇编手工编写的导入函数遵循C调用约定一样。 - didierc