C++ 在C+中向前声明枚举+;

C++ 在C+中向前声明枚举+;,c++,enums,C++,Enums,我正在尝试做如下事情: enum E; void Foo(E e); enum E {A, B, C}; 编译器拒绝了它。我快速浏览了一下谷歌,大家的共识似乎是“你做不到”,但我不明白为什么。有人能解释一下吗 澄清2:我这样做是因为我在一个类中有一个私有方法,该类接受所述枚举,我不希望该枚举的值被公开-因此,例如,我不希望任何人知道E被定义为 enum E { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY

我正在尝试做如下事情:

enum E;

void Foo(E e);

enum E {A, B, C};
编译器拒绝了它。我快速浏览了一下谷歌,大家的共识似乎是“你做不到”,但我不明白为什么。有人能解释一下吗

澄清2:我这样做是因为我在一个类中有一个私有方法,该类接受所述枚举,我不希望该枚举的值被公开-因此,例如,我不希望任何人知道E被定义为

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}
因为我不想让我的用户知道X项目

因此,我想向前声明enum,这样我就可以将私有方法放在头文件中,在cpp中内部声明enum,并将构建的库文件和头分发给用户


至于编译器-它是GCC。

似乎不能在GCC中向前声明


一个有趣的讨论是。

它似乎无法在GCC中向前声明


一个有趣的讨论是。

确实没有enum的转发声明。由于枚举的定义不包含任何可能依赖于使用枚举的其他代码的代码,因此在首次声明枚举时完全定义它通常不是问题

如果枚举的唯一用途是由私有成员函数使用,则可以通过将枚举本身作为该类的私有成员来实现封装。枚举仍然必须在声明点(即类定义内)完全定义。然而,这并不是一个更大的问题,因为在这里声明私有成员函数,并且也不是一个更糟糕的实现内部公开


如果您需要对实现细节进行更深入的隐藏,可以将其分解为一个抽象接口(仅由纯虚拟函数组成)和一个实现(继承)接口的具体、完全隐藏的类。类实例的创建可以由工厂或接口的静态成员函数来处理。这样,即使是真正的类名,更不用说它的私有函数,也不会被公开。

确实没有enum的前向声明。由于枚举的定义不包含任何可能依赖于使用枚举的其他代码的代码,因此在首次声明枚举时完全定义它通常不是问题

如果枚举的唯一用途是由私有成员函数使用,则可以通过将枚举本身作为该类的私有成员来实现封装。枚举仍然必须在声明点(即类定义内)完全定义。然而,这并不是一个更大的问题,因为在这里声明私有成员函数,并且也不是一个更糟糕的实现内部公开


如果您需要对实现细节进行更深入的隐藏,可以将其分解为一个抽象接口(仅由纯虚拟函数组成)和一个实现(继承)接口的具体、完全隐藏的类。类实例的创建可以由工厂或接口的静态成员函数来处理。这样,即使是真正的类名,更不用说它的私有函数,也不会被公开。

因为枚举可以是大小不同的整数大小(编译器决定给定枚举的大小),指向枚举的指针也可以有不同的大小,因为它是整数类型(例如,在某些平台上,字符具有不同大小的指针)


因此编译器甚至不能让您向前声明枚举并向用户提供指向它的指针,因为即使在那里,它也需要枚举的大小。

因为枚举可以是大小不同的整数大小(编译器决定给定枚举的大小),指向枚举的指针也可以有不同的大小,因为它是整数类型(例如,在某些平台上,字符具有不同大小的指针)


因此编译器甚至不能让您向前声明枚举并向用户提供指向它的指针,因为即使在那里,它也需要枚举的大小。

[我的答案是错误的,但我把它留在这里,因为注释很有用]

前向声明枚举是非标准的,因为指向不同枚举类型的指针不能保证大小相同。编译器可能需要查看定义才能知道此类型可以使用什么大小的指针


实际上,至少在所有流行的编译器中,枚举指针都是一致的大小。例如,使用Visual C++提供枚举的前向声明作为语言扩展。[/p>(我的答案是错误的,但我把它留在这里是因为注释有用)。 前向声明枚举是非标准的,因为指向不同枚举类型的指针不能保证大小相同。编译器可能需要查看定义才能知道此类型可以使用什么大小的指针

实践中,至少在所有流行的编译器中,枚举指针是一致的大小。例如,用Eng+ C++提供枚举的前向声明作为语言扩展。 [在公共标题中]

typedef unsigned long E;

void Foo(E e);
[在内部标题中]

enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
  FORCE_32BIT = 0xFFFFFFFF };
通过添加FORCE_32位,我们确保Econtent编译为long,因此它可以与E互换。

我会这样做:

[在公共标题中]

typedef unsigned long E;

void Foo(E e);
[在内部标题中]

enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
  FORCE_32BIT = 0xFFFFFFFF };

通过添加Frime32位,确保Eclipse编译为一个长的,因此它可以与E.< /P> < P>互换。EnUM不能向前声明的原因是,如果不知道这些值,编译器就不能知道EnUM变量所需的存储。C++编译器可以根据所需的大小指定实际的存储空间。包含所有指定的值。如果所有可见的都是转发声明,则翻译单元无法知道选择了什么存储大小–它可能是

char
,或
int
,或其他


< ISO> C++标准7.52.5:

枚举数的基础类型
class A
{
public:
    ...
private:
    void* pImpl;
};
class AImpl
{
public:
    AImpl(A* pThis): m_pThis(pThis) {}

    ... all private methods here ...
private:
    A* m_pThis;
};
((AImpl*)pImpl)->PrivateMethod();
enum X;
enum X : int;
typedef int myint; enum T ; void foo(T * tp ) { * tp = (T)0x12345678; } enum T : char { A }; ?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
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; }
namespace type
{
    class legacy_type;
    typedef const legacy_type& type;
}
// May be defined here or pulled in via #include.
namespace legacy
{
    enum evil { x , y, z };
}


namespace type
{
    using legacy::evil;

    class legacy_type
    {
    public:
        legacy_type(evil e)
            : e_(e)
        {}

        operator evil() const
        {
            return e_;
        }

    private:
        evil e_;
    };
}
#include "forward.h"

class foo
{
public:
    void f(type::type t);
};
#include "foo.h"

#include <iostream>
#include "enum.h"

void foo::f(type::type t)
{
    switch (t)
    {
        case legacy::x:
            std::cout << "x" << std::endl;
            break;
        case legacy::y:
            std::cout << "y" << std::endl;
            break;
        case legacy::z:
            std::cout << "z" << std::endl;
            break;
        default:
            std::cout << "default" << std::endl;
    }
}
#include "foo.h"
#include "enum.h"

int main()
{
    foo fu;
    fu.f(legacy::x);

    return 0;
}
enum Enum1;                     // Illegal in C++03 and C++11; no size is explicitly specified.
enum Enum2 : unsigned int;      // Legal in C++11.
enum class Enum3;               // Legal in C++11, because enum class declarations have a default type of "int".
enum class Enum4: unsigned int; // Legal C++11.
enum Enum2 : unsigned short;    // Illegal in C++11, because Enum2 was previously declared with a different type.
enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);
#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; \
}
enum E : short;
void foo(E e);

....

enum E : short
{
    VALUE_1,
    VALUE_2,
    ....
}