题
我试图做一些事情如下:
enum E;
void Foo(E e);
enum E {A, B, C};
哪个编译器的拒绝。我已经快看上谷歌和共识似乎是"你不能这样做",但我不能理解为什么。任何人都可以解释一下吗?
澄清2:我这么做是因为我有私人方法中的一类,采取所述枚举,我不想要这枚举的价值观暴露的-因此,例如,我不想让任何人知道该电子被定义为
enum E {
FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}
作为项目X不是我想要的东西我的用户了解。
因此,我要向前声明枚举,所以我可以把私人的方法,在标题的文件,宣布enum内部在加拿大养恤金计划,并且分发修建图书馆的文件和头的人。
作为用于编译器-这是海湾合作委员会.
解决方案
因枚举不可能是向前宣布是不知道的价值观,编译可能不知道存储所需的enum变量。C++编译器的允许指定实际的储存空间依据必须包含所有值指定。如果所有的是可见的,是向前的宣言,翻译股不能知道什么储存的大小会已经选择-这可能是一个char或诠释,或者别的东西。
从部分7.2.5的ISO C++标准:
的 基础类型 枚举是一个不可分割类型,可以代表所有数值定义在枚举。它是实现所定义的,其组成类型被用作基本类型列举除了基础类型不应大于
int
除非价值的枚举不适合在一个int
或unsigned int
.如果的 枚举名单 是空的,基本类型是,如果列举了一个单一的数值为0.值sizeof()
加枚举的类型,目的列举的类型,或枚举,值sizeof()
应用于潜在的类型。
由于 叫 要功能必须知道的尺寸的参数,以正确地设置呼叫叠,数枚举,在枚举名单必须知道以前的功能原型。
更新:C++0X一语法前言宣布枚举的类型,已提出和接受。你可以看到的建议 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf
其他提示
前进《宣言》的枚举,也可以在C++0x.以前,原因枚举的类型不可能向前宣布是因为大举取决于其内容。只要大小枚举的指定应用程序,它可以向前宣布:
enum Enum1; //Illegal in C++ and C++0x; no size is explicitly specified.
enum Enum2 : unsigned int; //Legal in C++0x.
enum class Enum3; //Legal in C++0x, because enum class declarations have a default type of "int".
enum class Enum4: unsigned int; //Legal C++0x.
enum Enum2 : unsigned short; //Illegal in C++0x, because Enum2 was previously declared with a different type.
我增加一个新的答案在这里,鉴于最近的事态发展。
你可以向前宣布一个枚举,在C++11,所以只要你宣布将其储存的类型在同一时间。法看起来是这样的:
enum E : short;
void foo(E e);
....
enum E : short
{
VALUE_1,
VALUE_2,
....
}
事实上,如果功能不指的是价值观的枚举,你不需要完整的声明在所有在这一点上。
这是支持通过G++4.6和起(-std=c++0x
或 -std=c++11
在更最近的版本)。视觉C++2013支持这一点;在较早的版本则有某种形式的非标准的支持,我还没有想出来呢-我发现了一些建议,一个简单的前进《宣言》是合法的,但情况因人而异。
向前宣布的事情在C++是非常有用的,因为它 急剧加快汇编的时间.你可以向前宣布几件事情在C++包括: struct
, class
, function
, 等等...
但你可以向前一个声明 enum
C++?
不,你不能。
但是,为什么不让呢?如果其被允许,你可以定义你的 enum
输入你的头文件,和你 enum
值在你的来源的文件。听起来就像它应该被允许的对吗?
错误的。
C++没有默认的类型 enum
像是在C#(int)。C++你 enum
类型将取决于编译器是任何类型,将适合的范围值,你有你的 enum
.
这是什么意思?
这意味着你 enum
's基础类型不能被充分确定的,直到你有所值的 enum
定义。其芒你不能独立的宣言和定义 enum
.因此你不能前进的声明 enum
在C++。
ISO C++标准S7。2.5:
底层类型的一枚举是一个不可分割类型,可以代表所有数值定义在枚举。它是实现所定义的,其组成类型被用作基本类型列举除了基础类型不应大于
int
除非价值的枚举不适合在一个int
或unsigned int
.如果枚举名单是空的,基本类型是,如果列举了一个单一的数值为0.值sizeof()
加枚举的类型,目的列举的类型,或枚举,值sizeof()
应用于潜在的类型。
你可以确定大小的一个列举的类型在C++通过使用 sizeof
操作员。大所列举的类型是其潜在的类型。以这种方式可以猜测这类编译器是用于 enum
.
什么,如果指定的类型 enum
明确这样的:
enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);
你可以再往前宣布你 enum
?
没有。但为什么不呢?
指定的类型 enum
不是部分实际上目前的C++的标准。这是一个VC++扩展。它将部分C++0x虽然。
[我的回答是错误的,但是我留在这里因为该意见是有用的].
向前宣布枚举是非标准,因为指针指向不同的枚举的类型是不能保证大小相同。编译器可能需要看到的定义来知道是什么大小的指针可以使用这种类型。
在实践中,至少在所有流行的编译器、指针,以枚举是一致的大小。前进《宣言》的枚举是作为一个语言扩展的视觉C++,例如。
确实是有没有这样的东西作为一个前进的《宣言》的枚举。作为一个枚举的定义不包含任何代码,可能取决于其他代码使用枚举,这是通常不是一个问题定义的枚举完全当你第一次宣布。
如果只有使用你的枚举,是通过私人成员的功能,可以实施的封装通过具有枚举本身作为一个私人部件的这一类。枚举仍有待充分定义的宣言,即在这一类别的定义。然而,这不是一个更大的问题,作为宣告私人部件的功能在那里,并不是一个糟糕的曝光的执行情况的内部。
如果你需要一个更深程度的隐蔽你的执行情况的详细信息,你可以把它变成一个抽象的界面,只有组成的纯粹的虚拟功能,并具体、完全隐瞒级的执行(继承)的接口。建立类的实例可以处理的工厂或者一个静态部件功能的接口。这样,即使实际流的名字,让我们单独其私人的功能,不会暴露出来。
只要注意到实际上的原因 是 小enum还不知道之后进《宣言》。好了,你使用前宣言》的结构,能够通过一个指周围,或是指一个对象从一个地方称为在向前宣布结构定义本身。
向前宣布一个枚举不会太有用的,因为一个希望能够通过围绕枚举的价值。你甚至不能有一个指针,因为我刚刚告诉一些平台使用的指针的不同大小char比int或长。因此,一切都取决于内容的枚举。
目前C++标准明确禁止在做喜欢的东西
enum X;
(在 7.1.5.3/1
).但接下来C++的标准,由于下一年允许下,我相信该问题实际上 已 要做到与基本类型:
enum X : int;
它被称为"透明"enum宣言》。你甚至可以使用X 通过价值 在以下代码。和它的调查员以后可以被限定在以后重新声明的枚举。看看 7.2
在目前的工作草案。
我会做这种方式:
[在公众的头]
typedef unsigned long E;
void Foo(E e);
[在内部标题]
enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
FORCE_32BIT = 0xFFFFFFFF };
通过增加FORCE_32BIT我们确保Econtent编制一个长期的,因此它是可以互换E.
看来,这不可能是向前宣布在海湾合作委员会的!
有趣的讨论 在这里,
如果你真的不想要你的枚举出现在你的头文件,并确保它只是用于由私人方法,那么一个解决方案可以去pimpl原则。
这是一种技术,确保以隐藏这类内部的头的只是声明:
class A
{
public:
...
private:
void* pImpl;
};
然后在你执行情况的文件(cpp),声明的一类,这将是表示的内部。
class AImpl
{
public:
AImpl(A* pThis): m_pThis(pThis) {}
... all private methods here ...
private:
A* m_pThis;
};
你必须动态创建的执行情况的类的构造和删除它析构和当实施公共方法,则必须使用:
((AImpl*)pImpl)->PrivateMethod();
有利于使用pimpl之一是,它分类标题从其执行情况,没有必要重新编译的其他类当改变一级的执行情况。另一个原因是,为加快汇编的时间,因为你的头那么简单。
但这是一个痛苦来使用,所以你真的应该问问自己,如果只是宣布你枚举,作为私人的头是一个麻烦。
你可以包枚举,在一个结构,添加一些构造和类型转换和向前声明的结构,而不是。
#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
还有一些异议,因为这得到了撞(排序),以下是一些相关的位的标准。研究显示,标准并没有真正定义进《宣言》,也不明确状态,这枚举可以或不可以向前宣布。
第一,从dcl.枚举,第7.2:
底层类型的一枚举 是一个不可分割类型,可以代表 所有数值定义 枚举。它是 执行定义,其组成 类型用作为基础的类型 一枚举的除了 基础类型不够大 比int除非价值 枚举不适合在一个int或 unsigned int。如果枚举名单 是空的,基本类型是因为如果 列举了一个单一的 数值为0.值 sizeof()加枚举 类型的目的列举的类型, 或枚举,值 sizeof()施加的基础 类型。
这样的基本类型的一个枚举,是执行定义的,与一个小小的限制。
接下来,我们翻转到该部的"不完整的类型"(3.9),这是关于关闭,因为我们来到任何标准上向前声明:
一类是已经宣布,但没有定义或一系列未知的大小或 不完整的元件的类型,是一个不完全的定义的对象的类型。
的类型(例如"X类")可能是不完整的在一个点,在一个翻译 单元完成以后;的类型"类X"是同一类型的两点。的 宣布类型的一系列的对象可能是一系列不完整的类型和 因此不完整;如果该类型完成以后的翻译单位, 阵列类型变得完整;阵列类型在这两点是相同的类型。该声明的类型的一系列的对象可能是一系列未知的尺寸,因此可以 不完整的在一个点,在一个翻译单位和稍后完成;阵列类型 这两点("阵列的未知约束的T"和"列N T")是不同的 类型。这种类型的指数组的未知的大小或类型的定义typedef 《宣言》是一系列未知的大小,无法完成。
所有的标准几乎规定的类型,可以向前宣布。枚举是不存在的,因此编译作者的一般方面向前宣布为不允许的标准由于可变的基础类型。
这是有道理的,也是。枚举,通常引用通过价值的情况,并编译器,将确实需要知道存储大小,在这些情况。由于储存的大小是执行定义,许多编制者使用时就会只是选择使用的32位的价值观为基础类型的每一枚举,在这一点成为可能向前声明他们。一个有趣的实验可能会尝试向前宣布一个枚举,在visual studio,然后迫使它利用一个基础类型大于sizeof(int)如上面所解释的,看看会发生什么情况。
VC,这是试验关于前向声明,并指定基本类型:
- 以下代码编制"确定"。
typedef int myint; enum T ; void foo(T * tp ) { * tp = (T)0x12345678; } enum T : char { A };
但得到的警告/W4(/W3不会产生这样的警告)
警告C4480:非标准延用于:指定潜在的类型enum"T"
VC(Microsoft(R)32-bit C/C++优化编译版本15.00.30729.01为80条x86) 看起来越野车在上述情况下:
- 当看到enum 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.asm直接的,不是我个人的猜测。你看到的 视双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和移动的定义function foo后枚举T的定义:结果是确定:
上述关键指令变为:
文字节PTR[eax],120;00000078H
最终的结果是:0x78,0x1,0x1,0x1
注意到本价值是没有被复盖
所使用的前进《宣言》的enum在VC被认为是有害的。
顺便说一句,不惊讶的是,该法对于《宣言》的基本类型是相同的作为其在C#。在实践我发现它的价值来保存3个字节通过指定的基础类型为char时谈到嵌入式系统,这是记忆有限的。
在我的项目,我通过了 名字空间的定枚举 技术来处理 enum
从传统和第3-缔约方组成。这里是一个例子:
前进。h:
namespace type
{
class legacy_type;
typedef const legacy_type& type;
}
枚举。h:
// 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_;
};
}
foo。h:
#include "forward.h"
class foo
{
public:
void f(type::type t);
};
foo.cc:
#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;
}
}
main.cc:
#include "foo.h"
#include "enum.h"
int main()
{
foo fu;
fu.f(legacy::x);
return 0;
}
注意, foo.h
头没有知道什么 legacy::evil
.唯一的文件,使用传统类型 legacy::evil
(:main.cc)需要包括 enum.h
.
我解决你的问题就是:
1使用int而不是枚举:宣布你整数在一个匿名的空间你CPP文件(不在标题):
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:创建一个完整的类有限的const实例样做。向前宣布的类,然后将它定义在加拿大养恤金计划文件,并instanciate只枚举样的价值观。我没有东西像是在C++和结果不是为满足需要,因为它需要一些代码模拟enum(复制建设、操作员=,等等)。
3:提议之前,利用私下宣布枚举。尽管事实上一个用户将会看到它的完整定义,它将无法使用它,也不使用私方法。所以你通常能够修改枚举和内容的现有方法,而不需要重新编译代码使用。
我的猜测是任何解决方案3或1。
因为枚举,可以是一个整体尺寸不同的大小(compiler决定其大小定枚举的有),指枚举,也可以有不同大小的,因为它是一个整体类型(chars有指针的一个不同的尺寸上的一些平台,例如)。
因此编译器甚至不能让你进-宣布枚举和用户指针,因为即使是存在的,它需要大小枚举。
定义列举限制的可能值要素的类型以有限的设置。这种限制是被强迫在编译时间。
当前宣布的事实,你会使用'有限的设置以后不增加任何价值:随后码需要知道的可能值,以便从中受益。
虽然编译器 是 关切大小的所列举的类型, 意图 枚举丢失的时候你正向声明。