C ++标准是否要求C-linkage函数是“noexcept”?

时间:2014-06-23 09:32:16

标签: c++ c c++11 language-lawyer noexcept

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

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

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

5 个答案:

答案 0 :(得分:18)

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

因此,我认为假设“C”链接意味着noexcept是错误的。

答案 1 :(得分:8)

嗯,我认为extern "C"只使用C-linkage,而不是C函数。它阻止编译器执行C++ name mangling

更直接 - 假设此代码。

// foo.cpp
extern "C" void foo()
{
    throw 1;
}

// bar.cpp
extern "C" void foo();
void bar()
{
    try
    {
        foo();
    }
    catch (int)
    {
        // yeah!
    }
}

答案 2 :(得分:1)

Marc van Leeuwen's answer 是正确的:查找当前的 working draft,似乎没有任何东西要求声明 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's answer 中提到的原因,我认为用户定义的 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”必须严格遵守。

答案 3 :(得分: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(...)对其进行捕获。
  • 未定义的行为可能会抛出。某些系统实际上确实实现了* NULL作为抛出异常,即使在编译C代码时也可能被catch(...)捕获。如果您在任何地方执行未定义的行为,那么一旦代码路径不可撤销地致力于达到未定义的行为,整个程序便会变得未定义,因此这可能导致C标准库函数抛出异常。

答案 4 :(得分:0)

AC 函数 foo 可以调用用 C++ 编码的函数 bar,声明为 extern "C",这样 bar 是(也许是间接的)throw-ing some C++ 异常。

C 函数 foo(可能被某个 C++ 函数调用)可以调用 longjmp,其运行时行为接近于异常抛出。

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