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,
....
}