【问题标题】:Forward declaring an enum in C headers used in C++ code在 C++ 代码中使用的 C 头文件中前向声明枚举
【发布时间】:2018-02-01 18:41:10
【问题描述】:

你不能 forward declare an enum in C++,但你可以在 C 中。

对于使用一些 C++ 代码的 C 代码库,有没有办法在 C 中使用前向声明的枚举,当在 C++ 中使用该标头时(在 extern "C" {..} 块内)不会导致错误?

例子:

extern "C" {
    enum MyEnum;
}
int main() { return 0; }

GCC 给出错误:

error: use of enum ‘MyEnum’ without previous declaration
  enum MyEnum;
       ^~~~~~

Clang 也失败了:

error: ISO C++ forbids forward references to 'enum' types
        enum MyEnum;

为了给出一些上下文,这是一个主要的 C 代码库,其中一个小的 C++ 模块恰好包含 C 代码的标头。我可以做一些 hack 让 C++ 忽略枚举,但我想知道 C++ 在这种情况下是否可以使用 C 头文件。


更新:已经注意到官方 C 规范不支持这一点。然而,这似乎是一些最广泛使用的编译器的事实标准:GCCClangMicrosoft Visual C++

【问题讨论】:

  • 这可能有帮助吗? stackoverflow.com/questions/71416/…
  • 您遇到了什么样的错误?请提供minimal reproducible example 并显示实际的错误消息
  • 什么?您不能在 C 中转发声明枚举。只能在 C++ 中。
  • @storyteller - 它在 C11 中工作,afaik 它也适用于旧 C 版本。
  • @ideasman42 - 直到它不是。并且不能保证每个编译器与 C++ 的互操作性都是相同的。没有标准的 C 解决方案。 “事实上的”标准在交叉编译时毫无意义。

标签: c++ c


【解决方案1】:

C 中枚举的前向声明毫无意义。 C 中的枚举仅引入整数枚举常量。他们没有介绍您想要使用的任何类型。每个枚举类型都是未指定的整数类型,与所有其他整数类型兼容,因此根本不存在任何类型安全性。

typedef enum e12 { ONE, TWO } e12;
typedef enum e34 { THREE, FOUR } e34;
int main () {
    e12 one = ONE;
    e34 three = THREE;
    one = three;
    three = ONE;
    return one + three;
}

这个 C 程序可以在 -Wall -Wextra -Wpedantic 上使用 GCC 干净地编译(当然它不能作为 C++ 程序工作)。

所以一个有用的可移植跨语言解决方案是

#ifdef __cplusplus
   enum MyEnum : int;
#else
   typedef int MyEnum; // 'enum MyEnum' would give no improvement over this
#endif

【讨论】:

  • 使用枚举而不是整数有一些优势 - 使用 GCC/Clang 有枚举感知警告(-Wenum-compare-Wswitch-enum),地址清理程序有 -fsanitize=enum - 虽然不能保证,但它们可用于发现代码中使用常规整数无法检测到的真正错误。
【解决方案2】:

This can be done in C++11,(请参阅@vasiliy-galkin 的回答),这是一个如何用于共享 C/C++ 标头的示例。

C 标头转发声明 MyEnum

#ifdef __cplusplus
extern "C" {
#endif

enum MyEnum
#ifdef __cplusplus
: int
#endif
;

#ifdef __cplusplus
}
#endif

【讨论】:

    【解决方案3】:

    您可以从 C++11 开始在 C++ 中前向声明枚举。它被称为“不透明声明”而不是“前向声明”,因为从技术上讲,它会产生一些不同的效果:枚举的大小在其不透明声明之后是已知的,而前向声明的类型则不是这种情况。

    但是,从日常角度来看,这是相同的想法:您可以声明您的枚举并在代码中进一步使用它。 From cppreference:

    enum-key attr(optional) nested-name-specifier(optional) identifier enum-base(optional) ;(2) (since C++11)

    2) 不透明枚举声明:定义枚举类型但不定义其枚举数:在此声明之后,类型是完整类型,并且其大小已知。注意:类模板的范围枚举成员的显式特化声明是 nested-name-specifier 出现在 identifier 之前的唯一情况(C++14 起)

    【讨论】:

    • @ideasman42 由于标头已经包含#ifdef __cplusplus,这仅意味着标头中应存在2个不同的前向声明-如果使用C+语法,则在内部-一个在#else内部,使用C语法...
    • @ArtemyVysotsky 当然不能保证 C++ 不透明声明与准 C 前向声明兼容。
    • @n.m - 同意,这可能是链接期间的问题
    猜你喜欢
    • 1970-01-01
    • 2010-09-09
    • 2010-10-31
    • 2011-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多