In C++, how can I avoid #including a header file when I need to use an enumeration?

被刻印的时光 ゝ 提交于 2019-12-12 08:09:11

问题


In my C++ header files I try to use forward declarations (class MyClass;) instead of #including the class header, as recommended in many C++ coding standards (the Google C++ Style Guide is one).

Unfortunately, when I introduce enumerations, I can't do the forward declaration any more. Like this:

//// myclass1.hpp ////

class MyClass1
{
    enum MyEnum1
    {
        Enum_A, Enum_B, Enum_C
    };
};

//// myclass2.hpp ////

// I want to avoid this
#include "myclass1.hpp"

// I'd prefer to do this (forward declaration)
class MyClass1;

class MyClass2
{
    // This is o.k.: I only need to forward declare MyClass1
    MyClass1* ptr;

    // This forces me to #include, but I don't want to!
    void func( MyClass1::MyEnum1 e );
};

The best solution I can think of so far is to replace enums with member constants:

//// myclass1.hpp  ////

MyClass1
{
    static const int Enum_A;
    static const int Enum_B;
    static const int Enum_C;
};

//// myclass1.cpp ////

const int Enum_A = 1;
const int Enum_B = 2;
const int Enum_C = 3;

In this case, though, the solution seems worse than the problem.

I'm currently looking through Large Scale C++ Software Design (Lakos) and Working Effectively with Legacy Code (Feathers) for dependency breaking techniques, but I haven't found a good solution yet.


回答1:


You cannot forward declare enum values - and your workaround is a step down the path to complete madness.

Are you experiencing any major compilation slowdowns caused by #including headers? If not, just #include them. Use of forward declarations is not "best practice" it is a hack.




回答2:


This is difficult to do nicely. Perhaps moving enums to a common header file would be a reasonable solution?

Edit: I know the question asked to avoid including a header file, but there's just no way (AFAIK) to do this. Moving enums to a separate header file at least minimises the amount of stuff in the header file you do need to include. It's certainly better than the craziness suggested in the question!




回答3:


You can use forward declarations only when you are declaring a pointer. If you are declaring a non-pointer variable, you will have to include the relevant header file.

Since an enum variable is not a pointer you can't use forward declarations. And I don't think there's an alternative solution.




回答4:


C++0x's strongly typed enums can be forward declared. GCC 4.4.0 and CodeGear C++Builder 2009 support strongly typed enums.

There are a few enum-like classes floating around like the (proposed but never finalized and accepted) Boost.Enum available for download from the Boost Vault at this link. Since Boost.Enums are classes, they can be forward declared.

However, just putting enums in a separate file (as in this answer) seems the simplest, best solution (barring C++0x suport).




回答5:


You can use template arguments to program against 'general' enum types. Much like this:

// enum.h
struct MyClass1 { enum e { cE1, cE2, cELast }; };

// algo.h
// precondition: tEnum contains enumerate type e
template< typename tEnum > typename tEnum::e get_second() { 
    return static_cast<typename tEnum::e>(1); 
}

// myclass1.h

// myclass.h
template< typename tClass1 >
class MyClass2
{
    tClass1 * ptr;
    void func( tClass1::e e );
};
// main.cpp
#include "enum.h"
#include "algo.h"
int main(){ return get_second<Enum>(); }



回答6:


I don't think (I can be proven incorrect) that you can forward declare an internal type, nor an enumeration. You will need the definition of the enclosing class to use the enum.

While most style guides enforce not including unnecessary headers, in your case the header is necessary. Other options you can consider if you really want to avoid the inclusion would be defining the enumeration outside of the class and including the header that defines the enum.




回答7:


Forward declaration of enumerations has actually been proposed by the C++ standards committee. See this paper (pdf). It would certainly be a good feature!




回答8:


If you are really running into compilation slowdowns because of header inclusion, the other option is to use an int instead of an enum. This is a rather unpopular approach since it degrades type safety. If you do take this approach, then I would also recommend adding code to programmatically do the bounds checking:

// in class1.h
class Class1 {
public:
    enum Blah {
       kFirstBlah, // this is always first
       eOne = kFirstBlah,
       ...
       kLastBlah // this is always last
    };
};

// in checks.h
#include <stdexcept>
namespace check {
template <typename T, typename U>
U bounds(U lower, T value, U upper) {
    U castValue = static_cast<U>(value);
    if (castValue < lower || castValue >= upper) {
        throw std::domain_error("check::bounds");
    }
    return castValue;
}
} // end check namespace

// in class2.h
class Class2 {
public:
    void func(int blah);
};

// in class2.cpp
#include "class2.h"
#include "class1.h"
#include "checks.h"

void Class2::func(int blah) {
    Class1::Blah blah_;
    blah_ = check::bounds(Class1::kFirstBlah, blah, Class1::kLastBlah);
}

It's not the prettiest solution, but it does solve the header dependency problem by moving some of the type safety that static compilation gives you into runtime code. I've use similar approaches in the past and found that a check namespace used in this way can make the resulting code almost as readable as enum based code with very little effort.

The caveat is that you do have to make an effort to write exception-safe code which I recommend regardless of whether you adopt this approach or not ;)



来源:https://stackoverflow.com/questions/681243/in-c-how-can-i-avoid-including-a-header-file-when-i-need-to-use-an-enumerati

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!