← 返回信息流
AI 资讯Hacker News·18 小时前

GCC 与 Clang 均未完全遵循 C++ 标准

原标题:Neither GCC nor Clang are compliant with standard C++

速览

近日一项分析指出,主流编译器 GCC 和 Clang 均未完全遵循标准 C++ 规范,存在多处与标准不符的行为。这一发现可能影响依赖标准语义的跨平台代码开发,开发者需注意编译器的实际实现差异。

AI 深度解读

背景

C++ 标准对函数类型定义了一种称为“语言链接”(language linkage)的属性,用于区分不同语言(如 C++、C 或其他实现定义的语言)中的函数调用约定。标准明确规定:具有不同语言链接的两个函数类型,即便其他方面完全一致,也是不同的类型。这一设计本意是为了保证不同语言之间调用约定的兼容性——例如,C 函数和 C++ 函数可能使用不同的参数传递方式或名字修饰规则,因此不应被混用。

然而,实际的主流编译器 GCC 和 Clang 并未在类型系统中存储语言链接信息。这意味着,它们会将具有不同语言链接的函数类型视为相同,从而与 C++ 标准的要求产生冲突。

核心内容

原文指出,C++ 标准中函数类型的语言链接是一个强约束,但 GCC 和 Clang 忽略了这一约束。具体表现为:

  • 使用 extern "C" 定义的函数类型与普通 C++ 函数类型被编译器视为同一类型。例如,以下代码中的 static_assert 本应失败(因为两种类型不同),但在 GCC 和 Clang 中却通过编译:

    #include <type_traits>
    extern "C" using c_func = void ();
    // 按照标准,此断言应失败,但实际不失败
    static_assert(std::is_same<c_func, void ()>::value);
    
  • 当函数重载接受不同语言链接的函数指针参数时,标准认为这两个重载是合法的(因为参数类型不同),但 GCC 和 Clang 都认为参数列表相同,从而违反了单一定义规则(ODR),导致编译错误。例如:

    extern "C" using c_func = void ();
    void f(c_func *) {}
    void f(void (*)()) {}
    

    这段代码本应编译通过,但 GCC 和 Clang 均报错,认为两个 f 函数重复定义。

原文作者认为,问题的根源不在 GCC 或 Clang,而在于 C++ 标准本身。标准要求语言链接作为类型系统的一部分,但实际平台(几乎所有平台)上 C 和 C++ 的调用约定完全相同,因此这一区分在实际中毫无意义。此外,GCC 和 Clang 无法更改其行为,因为如果现在加入语言链接信息,会导致 ABI 破坏——例如,extern "C" 函数类型在名字修饰(mangling)中会采用不同的编码,现有二进制代码将无法兼容。

关键要点

  • GCC 和 Clang 未在函数类型中存储语言链接信息,导致 extern "C" 函数类型与普通 C++ 函数类型被视为相同。
  • 这违反了 C++ 标准中“不同语言链接的函数类型是不同的类型”的明确规定。
  • 具体表现包括:std::is_same 错误地返回 true,以及重载函数指针参数时触发 ODR 违反。
  • 作者认为标准本身有误:既然所有主流平台上的 C 和 C++ 调用约定一致,标准应将该行为改为“实现定义”(implementation-defined),而非强制要求类型区分。
  • 编译器无法修复此问题,因为修复会带来 ABI 破坏:修改后的名字修饰规则会改变符号名称,导致现有库和二进制文件无法链接。
  • 当前 GCC 和 Clang 的行为在实践上合理,但理论上与标准不一致。

意义与影响

这一现象揭示了一个更广泛的现实:C++ 标准中的某些规定,由于历史原因或理想化设计,与主流编译器实现和实际平台特性存在脱节。GCC 和 Clang 选择不遵循标准,并非出于懒惰,而是因为标准的要求在现实中没有实际价值,且修正会带来不成比例的 ABI 兼容性成本。

对于开发者而言,这意味着:

  • 不应依赖语言链接对函数类型的区分来做类型层面的判断(如 std::is_same),因为主流编译器不会支持。
  • 在重载接受函数指针的函数时,应避免同时使用 extern "C" 和普通函数指针作为参数,否则会导致编译错误。
  • 跨语言调用(如 C++ 调用 C 库)时,应依赖编译器实际的行为(即不区分类型),而非标准文本。

此事也反映出 C++ 标准委员会在制定规范时,有时会忽略实现现实和平台一致性。未来可能需要对相关条款进行修订,使其从“强制要求”改为“实现定义”,以匹配编译器行为并消除歧义。在此之前,GCC 和 Clang 的现状将继续存在,开发者需要理解并适应这种标准与实现之间的差异。

查看原文 →sebsite.pw