26

我在标准中找不到任何强制声明为的函数的内容extern "C"noexcept无论是隐式还是显式。

然而,应该清楚的是 C 调用约定不能支持异常......或者是吗?

标准是否在我错过的地方提到了这一点?如果不是,为什么不呢?它只是作为某种实现细节而留下吗?

4

5 回答 5

20

据我所知,不能保证用“C”链接定义的函数不会抛出异常。该标准允许 C++ 程序调用具有“C”语言链接的外部函数,并定义用 C++ 编写的具有“C”语言链接的函数。因此,没有什么可以阻止 C++ 程序调用具有“C”语言链接的函数,该函数实际上是用 C++ 编写的(可能在另一个编译单元中,尽管这不是必需的)。这将是一件奇怪的事情,但很难排除。此外,我没有看到标准中的哪个地方说这样做会导致未定义的行为(事实上,由于标准无法定义未用 C++ 编写的函数的行为,

因此,我认为假设“C”链接意味着noexcept.

于 2014-06-23T11:39:49.763 回答
9

嗯,我假设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!
    }
}
于 2014-06-23T11:39:48.920 回答
2

Marc van Leeuwen 的回答是正确的:查看当前的工作草案,似乎没有什么要求声明的函数extern "C"是隐式noexcept的。有趣的是,标准 C++ 禁止抛出 C++ 标准库中的 C 标准库函数。这些函数本身通常被指定为extern "C"(但这是定义见16.4.3.3-2的实现)。看看第16.4.6.13条[限制异常处理] 和随附的脚注174175

C 标准库中的函数不应引发异常 [脚注 174],除非此类函数调用程序提供的引发异常的函数。 [脚注 175]

脚注 174:

  1. 也就是说,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”对于严格遵守是必要的。

于 2021-02-04T06:16:47.417 回答
0

AC 函数foo可以调用用barC++ 编码的函数,声明extern "C"的,例如bar(可能间接地)throw-ing 一些 C++ 异常。

AC 函数foo(可能被某些 C++ 函数调用)可以调用运行时行为接近异常抛出的 longjmp

IIRC,第一个 C++ 编译器(Cfront)生成longjmp了用于翻译throw(和setjmp翻译catch)的 C 代码。当然,C++ 析构函数使事情复杂化。

于 2021-02-04T06:40:13.123 回答
0

没有任何地方说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 标准库函数抛出异常。
于 2019-05-23T01:37:09.480 回答