2

I've found the following pattern used fairly commonly in our company's code.

struct Foo
{
    enum FType
    {
        TypeA,
        TypeB,
        Type_MAX
    };
};
typedef Foo::FType FooType;
[...]
FooType m_type;

My question is, what is the benefit of this? (Or, what problem does this avoid?) To be clear, I am wondering why they didn't just...

enum FooType
{
    TypeA,
    TypeB,
    Type_MAX
};
[...]
FooType m_type;

I can't ask the original programmer because they have been re-assigned, and it turns out that the designated subject matter expert in our company is, in fact, me.

If it helps, this code has been compiled at various times using many versions of MSVC, gcc, and clang for different target platforms... all pre C++11.

Can anyone see why this was done? Apologies in advance, if the answer turns out to be something trivial.

Edited to add: This is used inside classes. (When an enum is global, our style guide requires entries to begin with a common prefix, in order to distinguish them from other symbols.)

6
  • Possible duplicate of Should you declare enums inside or outside a class? Commented Jul 12, 2018 at 15:02
  • 4
    "it turns out that the designated subject matter expert in our company is, in fact, me" hehe :) Commented Jul 12, 2018 at 15:10
  • 1
    @LightnessRacesinOrbit I know that feeling. Someone tells that you are only one who may know and there is that urge to curl up after you had listened to the question. Or attack table with head and regret that you don't have something sharp on hand. Commented Jul 12, 2018 at 15:58
  • I always recite simple concept "class' name is also a namespace". It solves most of problems with understanding. Commented Jul 12, 2018 at 16:00
  • @Swift-FridayPie: See also: "The Expert - a sketch" Commented Jul 12, 2018 at 17:56

6 Answers 6

6

The perceived problem with plain old enums is that the enumerators become names in the scope where the enum is defined. As a result, you could say, for example,

FooType m_type;
m_type = TypeA;

If you had also defined a class names TypeA you'd have a conflict. Putting the enum inside a class means you have to use a scope qualifier to get at the name, which would remove the conflict. It also means you'd have to write

FooType m_type;
m_type = FooType::TypeA;

because the previous version wouldn't be valid.

A newer solution to this problem is scoped enums:

enum class FooType {
    TypeA,
    TypeB,
    Type_MAX
};

Now you can say

FooType m_type;
m_type = FooType::TypeA;

but not

m_type = TypeA;

One difference here, as @Jarod42 points out, is that an enumerator defined by a plain enum can be implicitly converted to int, while an enumerator from a scoped enum is not. So with the definition inside a class,

int i = FooType::TypeA;

is valid, and i gets the value 0. With a scoped enum it is not valid.

In both cases, a cast makes the conversion okay:

int i = static_cast<int>(FooType::TypeA);
Sign up to request clarification or add additional context in comments.

5 Comments

Note that enum class is not equivalent to enum in class: no implicit conversion with int.
@Jarod42 -- added. Thanks.
until c++11 compilers didn't recognize that enum is part of class' scope. Some could issue warning about using class' name.
@Swift-FridayPie -- are you saying that the C++ language definition required enumerators defined in an enum that is, in turn defined inside a class, be injected into global scope? I don't think that was ever the rule; do you have authority for that? Or are you talking about some particular compiler?
Thanks for this answer (and to those who wrote equivalent/similar answers below!) In retrospect this seems very obvious. Having said that.... the technique is usually used when the 'enum' is inside a class, in which case polluting the scope shouldn't be a problem. I think I'm going to chalk this up to a house style guide that is too eager to make prescriptions. Thanks again, everyone.
4

Most likely this was done to prevent polluting the global scope with the enum members. When you have

enum FooType
{
    TypeA,
    TypeB,
    Type_MAX
};

TypeA, TypeB and Type_MAX become names in the global scope. This could cause conflicts with other enums or just other names already in use. By placing the enum in a struct you limit the names to being the the scope of the struct. Another way this is accomplished is using a namespace.

C++11 offers enum class that keeps the enum members scoped to the enum itself so this is no longer needed if you can deal with the more strict controls enum class imposes.

1 Comment

Using a namespace is not ideal, since namespaces can be expanded anywhere else and names are often used unqualified.
1

There probably wasn't enum class in C++ when this code was written, so that was the only solution to avoid outer namespace pollution.

Struct is used here more like a namespace. Still the author probably wanted name of enum itself to be in outer namespace, which is done by typedef.

Comments

1

In older C++ versions the enum names are added to the surrounding namespace.

In your second example I can do:

m_type = TypeA;

In the original example you need to do:

m_type = FooType::TypeA;

If TypeA is something fairly common in your application, I can see why you wouldn't want to pollute the namespace around it.

Comments

1

You should always put enum's inside a struct (or a class) in pre-C++11. The reason for it is that it prevents pollution of the global namespace and it forces qualified names when using the members of the struct. i.e Foo::TypeA. Why? because otherwise someone else might decide to create a constant or another enum member named TypeA somewhere else in the code and there would be a name conflict.

The typedef is probably just for the convenience of not typing the fully qualified enum type name every time.

This only apply to pre C++11. C++11 has enum class which declares a scoped enum. You mentioned that this code was written before that.

Comments

0

My question is, what is the benefit of this? (Or, what problem does this avoid?)

It limits the individual enumeration identifiers into the namespace of the class:

int TypeA;

struct Foo
{
    enum FType
    {
        TypeA, // OK, Foo::TypeA does not conflict with ::TypeA
        TypeB,
        Type_MAX,
    };
};

enum FooType
{
    TypeA, // not OK, conflicts with the existing ::TypeA declaration
    TypeB,
    Type_MAX,
};

The programmer could have used a namespace instead, which might have been clearer.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.