我想做的事情如下:
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。
在c++中向前声明是非常有用的,因为它极大地加快了编译时间。你可以在c++中前向声明一些东西,包括:结构,类,函数,等等…
但是你能在c++中前向声明一个枚举吗?
不,你不能。
但为什么不允许呢?如果允许的话,你可以在头文件中定义枚举类型,在源文件中定义枚举值。听起来应该是允许的,对吧?
错了。
在c++中,没有像c# (int)中那样的enum默认类型。在c++中,你的枚举类型将由编译器决定为适合你的枚举值范围的任何类型。
这是什么意思?
这意味着在定义了枚举的所有值之前,不能完全确定枚举的底层类型。这意味着您不能将枚举的声明和定义分开。因此,在c++中不能前向声明枚举。
ISO c++标准S7.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++中,可以使用sizeof操作符确定枚举类型的大小。枚举类型的大小是其基础类型的大小。通过这种方式,您可以猜测编译器正在为枚举使用哪种类型。
如果你像这样显式地指定枚举的类型会怎样:
enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);
然后你可以向前声明你的枚举吗?
不。但为什么不呢?
指定枚举的类型实际上不是当前c++标准的一部分。它是一个vc++扩展。不过,它将成为c++ 0x的一部分。
源
因为这个被撞了(某种程度上),所以有一些不同意见,所以这里有一些来自标准的相关比特。研究表明,该标准并没有真正定义前向声明,也没有明确说明枚举可以或不可以前向声明。
首先,从dcl。Enum,第7.2节:
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.
因此枚举的底层类型是由实现定义的,有一个较小的限制。
接下来,我们翻到关于“不完整类型”的章节(3.9),这是关于前向声明的最接近的标准:
A class that has been declared but not defined, or an array of unknown size or of
incomplete element type, is an incompletely-defined object type.
A class type (such as "class X") might be incomplete at one point in a translation
unit and complete later on; the type "class X" is the same type at both points. The
declared type of an array object might be an array of incomplete class type and
therefore incomplete; if the class type is completed later on in the translation unit,
the array type becomes complete; the array type at those two points is the same type.
The declared type of an array object might be an array of unknown size and therefore be
incomplete at one point in a translation unit and complete later on; the array types at
those two points ("array of unknown bound of T" and "array of N T") are different
types. The type of a pointer to array of unknown size, or of a type defined by a typedef
declaration to be an array of unknown size, cannot be completed.
在这里,标准列出了可以前向声明的类型。Enum不存在,因此编译器作者通常认为标准不允许前向声明,因为其底层类型的大小是可变的。
这也说得通。枚举通常在按值的情况下引用,编译器确实需要知道这些情况下的存储大小。由于存储大小是实现定义的,许多编译器可能只选择为每个枚举的底层类型使用32位值,这时就可以转发声明它们。
一个有趣的实验可能是尝试在Visual Studio中向前声明枚举,然后强制它使用大于sizeof(int)的底层类型,看看会发生什么。
在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在最近的版本中)都支持这一点。Visual c++ 2013支持此功能;在早期版本中,它有一些我还没有弄清楚的非标准支持-我发现了一些建议,简单的向前声明是合法的,但您的哩数可能会有所不同。
我对你问题的解决方案是:
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。