我想做的事情如下:

enum E;

void Foo(E e);

enum E {A, B, C};

编译器拒绝它。我在谷歌上快速浏览了一下,共识似乎是“你做不到”,但我不明白为什么。有人能解释一下吗?

澄清2:我这样做是因为我在一个类中有私有方法,它采用所说的枚举,我不希望枚举的值暴露出来-因此,例如,我不希望任何人知道E被定义为

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}

项目X不是我想让我的用户知道的东西。

所以,我想要转发声明枚举,这样我就可以把私有方法放在头文件中,在cpp中内部声明枚举,并将构建的库文件和头分发给人们。

至于编译器——它是GCC。


当前回答

可以将枚举包装在结构体中,添加一些构造函数和类型转换,然后前向声明结构体。

#define ENUM_CLASS(NAME, TYPE, VALUES...) \
struct NAME { \
    enum e { VALUES }; \
    explicit NAME(TYPE v) : val(v) {} \
    NAME(e v) : val(v) {} \
    operator e() const { return e(val); } \
    private:\
        TYPE val; \
}

这似乎是有效的: http://ideone.com/TYtP2

其他回答

对于vc++,下面是关于前向声明和指定底层类型的测试:

下面的代码编译成功。

    typedef int myint;
    enum T ;
    void foo(T * tp )
    {
        * tp = (T)0x12345678;
    }
    enum T : char
    {
        A
    };

但是我得到了/W4的警告(/W3不招致这个警告)

使用非标准扩展名:为枚举“T”指定底层类型

vc++ (Microsoft (R) 32位C/ c++优化编译器版本15.00.30729.01 for 80x86)在上述情况下看起来有bug:

当看到枚举T时;VC假设枚举类型T使用默认的4字节int作为底层类型,因此生成的程序集代码为:

    ?foo@@YAXPAW4T@@@Z PROC                    ; foo
    ; File e:\work\c_cpp\cpp_snippet.cpp
    ; Line 13
        push    ebp
        mov    ebp, esp
    ; Line 14
        mov    eax, DWORD PTR _tp$[ebp]
        mov    DWORD PTR [eax], 305419896        ; 12345678H
    ; Line 15
        pop    ebp
        ret    0
    ?foo@@YAXPAW4T@@@Z ENDP                    ; foo

上面的汇编代码是从/Fatest中提取的。这不是我个人的猜测。

你看到了吗

mov DWORD PTR[eax], 305419896        ; 12345678H

行吗?

下面的代码片段证明了这一点:

    int main(int argc, char *argv)
    {
        union {
            char ca[4];
            T t;
        }a;
        a.ca[0] = a.ca[1] = a.[ca[2] = a.ca[3] = 1;
        foo( &a.t) ;
        printf("%#x, %#x, %#x, %#x\n",  a.ca[0], a.ca[1], a.ca[2], a.ca[3] );
        return 0;
    }

结果是:

0x78, 0x56, 0x34, 0x12

删除枚举T的前向声明,并将函数foo的定义移动到枚举T的定义之后:结果为OK:

上面的键指令变成:

mov BYTE PTR [eax], 120;00000078 h

最终结果为:

0x78, 0x1, 0x1, 0x1

注意,该值没有被覆盖。

因此,在vc++中使用前向声明enum被认为是有害的。

顺便说一句,毫不奇怪,底层类型的声明语法与c#中的相同。在实践中,我发现在与内存有限的嵌入式系统通信时,通过将底层类型指定为char来节省三个字节是值得的。

enum不能被前向声明的原因是,如果不知道值,编译器就不能知道enum变量所需的存储空间。c++编译器可以根据包含所有指定值所需的大小指定实际存储空间。如果所有可见的都是前向声明,那么翻译单元就不能知道所选择的存储大小是什么——它可能是char型、int型或其他类型。


ISO c++标准第7.2.5节:

The underlying type of an enumeration is an integral type that can represent all the enumerator values defined in the enumeration. It is implementation-defined which integral type is used as the underlying type for an enumeration except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int. If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0. The value of sizeof() applied to an enumeration type, an object of enumeration type, or an enumerator, is the value of sizeof() applied to the underlying type.

由于函数的调用者必须知道参数的大小才能正确地设置调用堆栈,因此枚举列表中的枚举数必须在函数原型之前知道。

更新:

在c++ 0X中,已经提出并接受了向前声明enum类型的语法。你可参阅前向申报枚举数(修订版3)的建议

对于iOS/Mac/Xcode上面临这种情况的人,

如果你在XCode中用Objective-C集成C/ c++头文件时遇到这个问题,只需将文件扩展名从。mm更改为。m即可

在GCC中似乎不能前向声明!

这里有一个有趣的讨论。

我对你问题的解决方案是:

1 -使用int代替enums:在CPP文件的匿名命名空间中声明你的int(而不是在头文件中):

namespace
{
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
}

因为你的方法是私有的,所以没有人会乱动数据。你甚至可以进一步测试是否有人发送了无效的数据:

namespace
{
   const int FUNCTIONALITY_begin = 0 ;
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
   const int FUNCTIONALITY_end = 3 ;

   bool isFunctionalityCorrect(int i)
   {
      return (i >= FUNCTIONALITY_begin) && (i < FUNCTIONALITY_end) ;
   }
}

2:像在Java中那样,用有限的const实例化创建一个完整的类。向前声明类,然后在CPP文件中定义它,并只实例化类似枚举的值。我在c++中做了类似的事情,结果并不像预期的那样令人满意,因为它需要一些代码来模拟枚举(复制构造,operator =等)。

3:如前所述,使用私有声明的enum。尽管用户将看到它的完整定义,但它不能使用它,也不能使用私有方法。因此,您通常可以修改枚举和现有方法的内容,而不需要使用您的类重新编译代码。

我猜答案不是3就是1。