我想做的事情如下:

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。


当前回答

事实上,没有所谓的提前宣布全体会议。由于枚举的定义不包含任何可能依赖于使用该枚举的其他代码的代码,因此在第一次声明枚举时完全定义它通常不是问题。

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

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

其他回答

事实上,没有所谓的提前宣布全体会议。由于枚举的定义不包含任何可能依赖于使用该枚举的其他代码的代码,因此在第一次声明枚举时完全定义它通常不是问题。

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

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

如果您真的不希望枚举出现在头文件中,并确保它只被私有方法使用,那么一种解决方案是遵循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有优点。一个是它将类头与其实现解耦,并且在更改一个类实现时不需要重新编译其他类。另一个原因是加快了你的编译时间,因为你的头文件太简单了。

但它使用起来很痛苦,所以你应该问问自己,仅仅在头文件中将枚举声明为private是否有那么大的麻烦。

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

#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

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

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

我对你问题的解决方案是:

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。