我想做的事情如下:

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

其他回答

在我的项目中,我采用了命名空间限定枚举技术来处理来自遗留组件和第三方组件的枚举。这里有一个例子:

forward.h:

namespace type
{
    class legacy_type;
    typedef const legacy_type& type;
}

enum.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。:

#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)的文件才需要包含枚举.h。

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)的建议

我会这样做:

[在公共标头中]

typedef unsigned long E;

void Foo(E e);

[在内部头文件中]

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

通过添加FORCE_32BIT,我们可以确保Econtent编译成一个long,这样它就可以和E。

我只是注意到,原因实际上是,在向前声明后,enum的大小还不知道。你使用一个结构的前向声明来传递一个指针,或者从一个在前向声明的结构定义本身中引用的地方引用一个对象。

向前声明枚举不是很有用,因为人们希望能够按值传递枚举。你甚至不能有指向它的指针,因为我最近被告知一些平台使用不同大小的指针用于char和int或long。所以这一切都取决于枚举的内容。

当前的c++标准明确禁止这样做

enum X;

(在7.1.5.3/1)。但下一个c++标准将于明年发布,允许以下功能,这让我确信问题实际上与底层类型有关:

enum X : int;

它被称为“不透明”枚举声明。您甚至可以在以下代码中按值使用X。并且它的枚举数可以稍后在枚举的重新声明中定义。参见当前工作草案中的7.2。

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

#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