Equivalent of "Using Namespace X" for Scoped Enumerations

Equivalent of using namespace X for scoped enumerations?

So, is there a way to avoid having to type CatState:: all the time?

Not before C++20. Just as there's no equivalent for having to type ClassName:: for static class members. You can't say using typename ClassName and then get at the internals. The same goes for strongly typed enums.

C++20 adds the using enum X syntax, which does what it looks like.

You can of course not use enum class syntax, just using regular enums. But then you lose strong typing.

It should be noted that one of the reasons for using ALL_CAPS for weakly typed enums was to avoid name conflicts. Once we have full scoping and strong typing, the name of an enum is uniquely identified and cannot conflict with other names. Being able to bring those names into namespace scope would reintroduce this problem. So you would likely want to use ALL_CAPS again to help disambiguate the names.

namespaces for enum types - best practices

Original C++03 answer:

The benefit from a namespace (over a class) is that you can use using declarations when you want.

The problem with using a namespace is that namespaces can be expanded elsewhere in the code. In a large project, you would not be guaranteed that two distinct enums don't both think they are called eFeelings

For simpler-looking code, I use a struct, as you presumably want the contents to be public.

If you're doing any of these practices, you are ahead of the curve and probably don't need to scrutinize this further.

Newer, C++11 advice:

If you are using C++11 or later, enum class will implicitly scope the enum values within the enum's name.

With enum class you will lose implicit conversions and comparisons to integer types, but in practice that may help you discover ambiguous or buggy code.

Use 'using enum' in C++20 in classes possible?

Yes, Foo::Red will work fine. using enum E behaves as, from [enum.udecl]:

A using-enum-declaration introduces the enumerator names of the named enumeration as if by a using-declaration for each enumerator.

And the standard contains an example of exactly this case:

[ Note: A using-enum-declaration in class scope adds the enumerators of the named enumeration as members to the scope. This means they are accessible for member lookup. [ Example:

enum class fruit { orange, apple };
struct S {
using enum fruit; // OK, introduces orange and apple into S
};
void f() {
S s;
s.orange; // OK, names fruit​::​orange
S::orange; // OK, names fruit​::​orange
}

— end example ] — end note ]


Note however that there is some controversy around the particular spelling for this feature. enum E is what's known as an elaborated type specifier (much like class C or struct S). Typically, elaborated type specifiers behave exactly the same was as their underlying versions. Elaborating is just meant to disambiguate, and you rarely need to disambiguate, so you wouldn't see it very often. However, in this particular case, using enum E and using E actually mean wildly different and wholly unrelated things. So keep in mind that there is a chance that this feature may not yet actually make C++20, despite currently being in the working draft and even having been published in the CD. As such, it's unlikely that compilers will implement this feature until they're sure it's necessary to implement (C++20 isn't exactly lacking in work for compiler writers...)

A way to use all the unqualified names in a C++0x enum class?

There is no way to do this in C++11. Just in case you are not aware of it - you get the E::Val1 notation even for an unscoped enumeration. For such an enumeration, you have Val1 accessible with and without the use of E::.

But you cannot take a scoped enumeration and selectively make all its enumerators visible in a given scope. It should also be noted that you can not write using E::Val1. The spec explicitly forbids this, your compiler just doesn't reject it yet.

Why cannot forward declare a scoped enum?

At least, if forward-declare an enum was allowed, it would have created problems with template specializations like the one in the following example:

// somewhere in a .cpp

template<typename>
struct S;

enum S<int>::E;

// somewhere in a galaxy far, far away

template<typename>
struct S { enum class E {}; };

template<>
struct S<int> {};

How could the compiler know (and verify) that enum S<int>::E; is actually defined?


That said, even when you deal with namespaces you cannot do this:

struct X::A;
namespace X { struct A {}; }

But you can do this:

namespace X { struct A; }
namespace X { struct A {}; }

Using classes would result in a code like the following one:

struct A { enum E; };
struct A { enum E {} };

Anyway, this would violate the odr and it is not allowed.


Now, I'll try to give you my impression about the why.

If a forward-declaration of that type was allowed, you would have been allowed to give a partial definition of the containing class.

In other terms, consider this: enum S::E. This states firmly that S contains the enum class E, thus you are giving a clue about the definition of S. To speak not in standardese (that is far from being my natural language) you are partially defining S, thus the compiler should know that S has its definition somewhere plus it must have a definition for E too (either as part of the primary definition or as an out-of-class definition).

This would break the odr rules when the actual definition comes into view, so it cannot be allowed in any case, but as an exception of the basics rules of the language.

Moreover, this is a great source of headaches.

My two cents.

scoped but semi-weakly typed enumerations

For the initialization there is not much you can do other than cast the value. Note that C++11's strongly typed enums are not meant to replace but rather complement the existing enums. If you want a weakly-typed enum, just don't use enum class.

For the comparison of a strongly-typed enum, you can declare the necessary operators and cast inside the implementation:

bool operator==( uint32_t lhs, SystemEvents rhs )
{
return static_cast< SystemEvent >( lhs ) == rhs;
}

Of course you need both directions:

bool operator==( SystemEvents lhs, uint32_t rhs );

And other operators like !=.

c++ enum scope failed to compile with -std=c++98, but ok with -std=c++11

Enum values are not scoped by the enum type (either in C++98 or C++11). In the following example:

namespace N {
enum E { X };
}

X is directly in the scope of namespace N. Its fully qualified identifier would be ::N::X.

This behaviour was changed by C++11, where following the same definition, the identifier of X can also be referred using ::N::E::X:

[dcl.enum/11]


An enumerator declared in class scope can be referred to using the class member access
operators (::, . (dot) and -> (arrow)), see 5.2.5. [ Example:

struct X {
enum direction { left=’l’, right=’r’ };
int f(int i) { return i==left ? 0 : i==right ? 1 : 2; }
};

void g(X* p) {
direction d; // error: direction not in scope
int i;
i = p->f(left); // error: left not in scope
i = p->f(X::right); // OK
i = p->f(p->left); // OK
// ...
}

—end example ]

Scoped enumerations

You simply need to declare the type before using it, like so:

class Resource {
protected:
enum class ResourceType : int {
TEXT = 0
, PADDLE
, BALL
, BRICK
};
private:
ResourceType _id;
public:
Resource(ResourceType resourceID) : _id{ resourceID } {}
};

Why is using namespace X; not allowed at class/struct level?

I don't know exactly, but my guess is that allowing this at class scope could cause confusion:

namespace Hello
{
typedef int World;
}

class Blah
{
using namespace Hello;
public:
World DoSomething();
}

//Should this be just World or Hello::World ?
World Blah::DoSomething()
{
//Is the using namespace valid in here?
}

Since there is no obvious way of doing this, the standard just says you can't.

Now, the reason this is less confusing when we're talking namespace scopes:

namespace Hello
{
typedef int World;
}

namespace Other
{
using namespace Hello;
World DoSomething();
}

//We are outside of any namespace, so we have to fully qualify everything. Therefore either of these are correct:

//Hello was imported into Other, so everything that was in Hello is also in Other. Therefore this is okay:
Other::World Other::DoSomething()
{
//We're outside of a namespace; obviously the using namespace doesn't apply here.
//EDIT: Apparently I was wrong about that... see comments.
}

//The original type was Hello::World, so this is okay too.
Hello::World Other::DoSomething()
{
//Ditto
}

namespace Other
{
//namespace Hello has been imported into Other, and we are inside Other, so therefore we never need to qualify anything from Hello.
//Therefore this is unambiguiously right
World DoSomething()
{
//We're inside the namespace, obviously the using namespace does apply here.
}
}


Related Topics



Leave a reply



Submit