'enum class' values in parent scope?

241 views
Skip to first unread message

Matthew Woehlke

unread,
Jan 2, 2014, 12:05:18 PM1/2/14
to std-dis...@isocpp.org
Is there any way (that doesn't violate DRY) to make the values of a
scoped enum (i.e. 'enum class') accessible to the parent scope, as with
a traditional enum?

If not, would a method to do so be a reasonable addition to the standard?

--
Matthew

Filip Roséen

unread,
Jan 2, 2014, 12:42:15 PM1/2/14
to std-dis...@isocpp.org
You'd need to repeat all the names previously declared in the enumerator-list, if this counts as a violation of Don't Repeat Yourself.. I guess you are out of luck.

enum class E { A, B, C };

...

using E::A;
using E::B;
using E::C;

E x = A;


The above results in a compile-error when using `gcc` (`clang` accepts it), but it is valid according to the standard.




--
Matthew

--

--- You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+unsubscribe@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.

Matthew Woehlke

unread,
Jan 2, 2014, 12:50:50 PM1/2/14
to std-dis...@isocpp.org
On 2014-01-02 12:42, Filip Roséen wrote:
> On Thu, Jan 2, 2014 at 6:05 PM, Matthew Woehlke wrote:
>> Is there any way (that doesn't violate DRY) to make the values of a scoped
>> enum (i.e. 'enum class') accessible to the parent scope, as with a
>> traditional enum?
>
> You'd need to repeat all the names previously declared in the
> enumerator-list, if this counts as a violation of *Don't Repeat Yourself*..
> I guess you are out of luck.

Repeating the value list definitely violates DRY. (If I change the enum,
I'd have to change two places. That's bug fodder; not good.)

> enum class E { A, B, C };
>
> ...
>
> using E::A;
> using E::B;
> using E::C;
>
> E x = A;
>
> *The above results in a compile-error when using `gcc` (`clang` accepts
> it), but it is valid according to the standard.*

Eew... perhaps another reason to add a language feature to do this instead?

--
Matthew

David Rodríguez Ibeas

unread,
Jan 2, 2014, 1:45:48 PM1/2/14
to std-dis...@isocpp.org
Why are you using a scoped enum if you want the enumerators to be unscoped? Not saying that there is no good reason, just curious.

   David




--
Matthew

Filip Roséen

unread,
Jan 2, 2014, 2:06:45 PM1/2/14
to std-dis...@isocpp.org
NOTE: After a discussion with `V-ille` in ##c++ @ freenode it seems to be that `using E::A` after `enum class E { A };` is actually disallowed, and what I wrote about the bug being in gcc and not in clang is false (it's the other way around with the current standard).


To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.

Ville Voutilainen

unread,
Jan 2, 2014, 2:07:41 PM1/2/14
to std-dis...@isocpp.org
On 2 January 2014 19:42, Filip Roséen <filip....@gmail.com> wrote:
> You'd need to repeat all the names previously declared in the
> enumerator-list, if this counts as a violation of Don't Repeat Yourself.. I
> guess you are out of luck.
>
> enum class E { A, B, C };
>
> ...
>
> using E::A;
> using E::B;
> using E::C;
>
> E x = A;
>
>
> The above results in a compile-error when using `gcc` (`clang` accepts it),
> but it is valid according to the standard.

I don't read the wording as that being valid. [namespace.udecl]/7
says "A using-declaration shall not name a scoped enumerator."
[dcl.enum]/2 says " The enum-keys enum class and enum struct are semantically
equivalent; an enumeration type declared with one of these is a scoped
enumeration,
and its enumerators are scoped enumerators. "

Based on that, gcc is correct to reject that using-declaration and
clang is incorrect
to accept it. However, see also
http://open-std.org/JTC1/SC22/WG21/prot/14882fdis/cwg_active.html#1742

Ville Voutilainen

unread,
Jan 2, 2014, 2:08:37 PM1/2/14
to std-dis...@isocpp.org
On 2 January 2014 21:07, Ville Voutilainen <ville.vo...@gmail.com> wrote:
> to accept it. However, see also
> http://open-std.org/JTC1/SC22/WG21/prot/14882fdis/cwg_active.html#1742

Pardon the wrong link, try this instead
http://open-std.org/JTC1/SC22/WG21/docs/cwg_active.html#1742

Matthew Woehlke

unread,
Jan 2, 2014, 2:10:01 PM1/2/14
to std-dis...@isocpp.org
On 2014-01-02 13:45, David Rodríguez Ibeas wrote:
> Why are you using a scoped enum if you want the enumerators to be unscoped?
> Not saying that there is no good reason, just curious.

For the type safety, *not* for the scoping...

I'd like to be able to make a minimal change to get type safety.
"Minimal" meaning both that I don't want to have to change existing
users (that are using the enum correctly, anyway, which ideally is all
of them), and also I have some cases where I feel that the API design is
better to have the enum values belong to the parent class vs. some other
scope.

--
Matthew

Diego Sánchez

unread,
Jan 2, 2014, 2:12:39 PM1/2/14
to std-dis...@isocpp.org
I'm exactly in the same situation here. It would be nice to be able to decouple both features.


2014/1/2 Matthew Woehlke <mw_t...@users.sourceforge.net>

David Krauss

unread,
Jan 2, 2014, 9:09:32 PM1/2/14
to std-dis...@isocpp.org
On 1/3/14 3:07 AM, Ville Voutilainen wrote:
I don't read the wording as that being valid. [namespace.udecl]/7
says "A using-declaration shall not name a scoped enumerator."
[dcl.enum]/2 says " The enum-keys enum class and enum struct are semantically
equivalent; an enumeration type declared with one of these is a scoped
enumeration,
and its enumerators are scoped enumerators. "

Yep. Here's a workaround macro which wraps an unscoped enumeration in a class to allow the scoping restriction to be bypassed by derived classes. (Basically the same as you could do before C++11.)

#define IMPORTABLE_ENUM( TYPENAME, ... ) \
struct TYPENAME ## _import_base \
    { enum TYPENAME { __VA_ARGS__, TYPENAME ## _last }; }; \
typedef TYPENAME ## _import_base :: TYPENAME TYPENAME;

usage:

IMPORTABLE_ENUM( token_type, ws, id, num, punct, string_lit, ud_string_lit, char_lit, ud_char_lit, header_name, misc )

class lexer : token_type_import_base {


But, this type still implicitly converts to int.

I've almost eliminated that macro from my codebase, and on second thought a wrapping namespace would be better.

(From the DR) Should these rules be relaxed?

On one hand, to break the scoping is a step backward. There's really no enumerator that should never be scoped (although limited unscoped accessibility may avoid a lot of unnecessary repetition). On the other hand, the scoping rules are pretty draconian. There's no reason not to be able to import names from namespace scope to class scope, or export static members from class scope. This doesn't address DRY though.

On the second hand, the implicit conversion rule is a bit broken because there is no way to specify explicit conversions, and unscoped implicit conversion is always to int regardless of the enumeration's explicit underlying type. Fixing this issue could allow broader use of unscoped enumerations.

On the one foot, enumerations would benefit from single inheritance, which the above macro also hackishly half-supports. (Inheritance also requires some kind of implicit conversion, for which currently only implicit unscoped conversion to int may apply.) If you derived an unscoped enumeration from a scoped enumeration, the enumerators could effectively be exported. On the other foot, that could be considered an abuse and disallowed. Two unscoped enumerations in the same scope could perhaps not be derived from the same base, depending how name lookup were specified.

Andrew Tomazos

unread,
Jan 3, 2014, 12:40:37 AM1/3/14
to std-dis...@isocpp.org
On Thursday, January 2, 2014 6:42:15 PM UTC+1, Filip Roséen wrote:
You'd need to repeat all the names previously declared in the enumerator-list, if this counts as a violation of Don't Repeat Yourself.. I guess you are out of luck.

enum class E { A, B, C };

...

using E::A;
using E::B;
using E::C;

E x = A;
 
That's ill-formed.  What you want to write is this:

constexpr E A = E::A;
constexpr E B = E::B;
constexpr E C = E::C;

Matthew Woehlke

unread,
Jan 3, 2014, 11:40:22 AM1/3/14
to std-dis...@isocpp.org
On 2014-01-02 21:09, David Krauss wrote:
> On 1/3/14 3:07 AM, Ville Voutilainen wrote:
>> (From the DR) Should these rules be relaxed?
>
> On one hand, to break the scoping is a step backward. There's really no
> enumerator that should never be scoped (although limited unscoped
> accessibility may avoid a lot of unnecessary repetition).

If you mean no enum values should exist in the global scope, then
definitely I agree :-). I have some cases however where I really feel
the API is more "natural" to have certain enum values in a class's
scope, e.g. where the enum values are effectively properties of that
class, and/or closely tied to the use of the class. (To borrow an
example from Qt, QDialog::Accepted, etc.)

Unless I hear it's a terrible idea, I think I will propose adding
'inline enum class' to the next revision of the standard. The effect
would be exactly as with 'inline namespace'; the contents of the named
scope are made accessible also via the parent scope. The other semantics
of 'enum class' would not be affected.

> On the second hand, the implicit conversion rule is a bit broken because
> there is no way to specify explicit conversions, and unscoped implicit
> conversion is always to int regardless of the enumeration's explicit
> underlying type. Fixing this issue could allow broader use of unscoped
> enumerations.

Funny you should mention this... I have a related idea to allow 'enum
class' to be more like 'class'; in particular to be able to define
constructors and conversion operators (std::string anyone?).

> On the one foot, enumerations would benefit from single inheritance,

Again... funny you should mention this... ;-)

--
Matthew

gmis...@gmail.com

unread,
Oct 30, 2014, 11:05:07 PM10/30/14
to std-dis...@isocpp.org, mw_t...@users.sourceforge.net

Unless I hear it's a terrible idea, I think I will propose adding
'inline enum class' to the next revision of the standard. The effect
would be exactly as with 'inline namespace'; the contents of the named
scope are made accessible also via the parent scope. The other semantics
of 'enum class' would not be affected.

 
I like this idea.

Thiago Macieira

unread,
Oct 30, 2014, 11:18:04 PM10/30/14
to std-dis...@isocpp.org
Maybe merge with the using namespace for classes option and just use the enum
to bring its identifiers into the scope.

struct X
{
enum class E {
e0,
e1
};
using namespace E;
};
using namespace X;

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

gmis...@gmail.com

unread,
Oct 30, 2014, 11:38:16 PM10/30/14
to std-dis...@isocpp.org
Why when inline enum is so much easier?

Myriachan

unread,
Oct 31, 2014, 1:17:12 AM10/31/14
to std-dis...@isocpp.org, gmis...@gmail.com

Because it breaks existing code.  Linkage/storage class specifiers...whatever they're called...before "class C", "struct S", "enum E" are parsed as referring to what comes after it, not the type itself.  This is why attributes go after the class keyword and such...I think.  (Attributes before the class keyword would refer to the object being declared as that class type or to the function being declared.)


enum Purr : short;

inline enum Purr GetMood();

enum Purr : short
{
  NONE
,
  WEAK
,
  STRONG
,
};

inline Purr GetMood()
{
 
return Purr::NONE;
}


An inline enum would have to use the awkward syntax "enum inline E".

gmis...@gmail.com

unread,
Oct 31, 2014, 3:40:19 AM10/31/14
to std-dis...@isocpp.org, gmis...@gmail.com
Why when inline enum is so much easier?


Because it breaks existing code.  Linkage/storage class specifiers...whatever they're called...before "class C", "struct S", "enum E" are parsed as referring to what comes after it, not the type itself.  This is why attributes go after the class keyword and such...I think.  (Attributes before the class keyword would refer to the object being declared as that class type or to the function being declared.)


enum Purr : short;

inline enum Purr GetMood();

enum Purr : short
{
  NONE
,
  WEAK
,
  STRONG
,
};

inline Purr GetMood()
{
 
return Purr::NONE;
}


An inline enum would have to use the awkward syntax "enum inline E".

It hadn't occurred to me it would work like that. I had figured. 

// Make no assumption about inline/class type.
enum Purr : short;

inline Purr GetMood();

inline enum Purr : short

{
  NONE
,
  WEAK
,
  STRONG
,
};

inline Purr GetMood()
{
 
return Purr::NONE;
}


"enum inline" still seems preferable to me than the namespace juggling in Thiago's example.

But if it's not possible, so bet it.

Matthew Woehlke

unread,
Oct 31, 2014, 11:09:42 AM10/31/14
to std-dis...@isocpp.org
On 2014-10-30 23:17, Thiago Macieira wrote:
> On Thursday 30 October 2014 20:05:07 gmis...@gmail.com wrote:
>>> Unless I hear it's a terrible idea, I think I will propose adding
>>> 'inline enum class' to the next revision of the standard. The effect
>>> would be exactly as with 'inline namespace'; the contents of the named
>>> scope are made accessible also via the parent scope. The other semantics
>>> of 'enum class' would not be affected.
>>
>> I like this idea.
>
> Maybe merge with the using namespace for classes option and just use the enum
> to bring its identifiers into the scope.
>
> struct X
> {
> enum class E {
> e0,
> e1
> };
> using namespace E;
> };
> using namespace X;

Yes, I've generally been convinced that 'using enum Enum' is preferable.
(I haven't gotten around to writing a paper yet... if anyone wants to
take this on and is interested in help, give a shout...)

Why? It's not *that* much harder to use, and adds the advantage that you
can inject the names into any scope, not just the one containing the
enum. Also, most folks here seem to prefer that syntax.

--
Matthew

Sean Hunt

unread,
Oct 31, 2014, 11:13:04 AM10/31/14
to std-dis...@isocpp.org
On Thursday, October 30, 2014 11:18:04 PM UTC-4, Thiago Macieira wrote:
On Thursday 30 October 2014 20:05:07 gmis...@gmail.com wrote:
> > Unless I hear it's a terrible idea, I think I will propose adding
> > 'inline enum class' to the next revision of the standard. The effect
> > would be exactly as with 'inline namespace'; the contents of the named
> > scope are made accessible also via the parent scope. The other semantics
> > of 'enum class' would not be affected.
>
> I like this idea.

Maybe merge with the using namespace for classes option and just use the enum
to bring its identifiers into the scope.

I think this is preferable because it is more flexible.

inline namespaces primarily serve a particular purpose (ABI versioning) that isn't shared with enum classes. Using-directives don't replace inline namespaces for a few technical reasons. Those reasons generally don't apply here, and a using-directive lets you do things like:

namespace A {
  namespace B {
    enum class E {
      Z,
    };
  }
}

using namespace A::B::E;

which you could not do with only a feature that allows you to import the enumerators to their enclosing scope.

Sean

David Rodríguez Ibeas

unread,
Oct 31, 2014, 12:02:36 PM10/31/14
to std-dis...@isocpp.org
An enum is *not* a namespace, and I find the reuse of the "using directive" a bad choice as you don't want to mimic the weirdness of using directives or provide different behavior with the same syntax, but this could be solved by using a different syntax (some bikeshedding below).

The core underlying design difference between the 'inline' and the 'using' approaches is that in the 'inline' approach it is the programmer providing the enum the one that needs to make the decision of whether the enumerators will be available in the enclosing scope or only nested, while in the 'using' approach is the programmer using the enum the one that chooses how to access the enumerators. My gut prefers the 'inline' direction, but the 'using' approach seems to be more flexible.

On the bikeshedding, different alternatives to "using namespace Enum" could include:

- Since 'enum' is not a 'namespace', just replace 'namespace' with 'enum': "using enum Enum;"

- Overload the meaning of the scope operator when immediately following the keyword: "using Enum::*;". This provides for quite natural code, but it is a bigger change to the grammar, and I am not sure how far could this be taken (could "using MyClass::*;" make sense?).

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.

Matthew Woehlke

unread,
Oct 31, 2014, 12:31:46 PM10/31/14
to std-dis...@isocpp.org
On 2014-10-31 12:02, David Rodríguez Ibeas wrote:
> The core underlying design difference between the 'inline' and the 'using'
> approaches is that in the 'inline' approach it is the programmer providing
> the enum the one that needs to make the decision of whether the enumerators
> will be available in the enclosing scope or only nested, while in the
> 'using' approach is the programmer using the enum the one that chooses how
> to access the enumerators.

Pedantic: not exclusive, with "using", either the consuming programmer
*or* API designer can make the choice. With "inline", only the API
designer can choose.

> - Since 'enum' is not a 'namespace', just replace 'namespace' with 'enum':
> "using enum Enum;"

I also find "using namespace Enum"... strange. I'd prefer that over not
having the feature at all, but I definitely also prefer "using enum
Enum" over "using namespace Enum".

> - Overload the meaning of the scope operator when immediately following the
> keyword: "using Enum::*;". This provides for quite natural code, but it is
> a bigger change to the grammar, and I am not sure how far could this be
> taken (could "using MyClass::*;" make sense?).

See
https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/A3Poct2ZHy8,
where this is being discussed on -proposals, which also suggested a
similar feature for classes. (Again, I'd prefer "using class" for that
case.) So at least one person thinks that is also useful...

--
Matthew

Myriachan

unread,
Oct 31, 2014, 4:57:31 PM10/31/14
to std-dis...@isocpp.org, mw_t...@users.sourceforge.net
On Friday, October 31, 2014 9:31:46 AM UTC-7, Matthew Woehlke wrote:
On 2014-10-31 12:02, David Rodríguez Ibeas wrote:
> - Since 'enum' is not a 'namespace', just replace 'namespace' with 'enum':
> "using enum Enum;"

I also find "using namespace Enum"... strange. I'd prefer that over not
having the feature at all, but I definitely also prefer "using enum
Enum" over "using namespace Enum".


My issue with "using enum Enum", "using class Class", etc. is that a traditional interpretation would be that the enum/struct/class/union keyword is being used as an elaborated type rather than a special statement.  If it were an elaboration tag, then the statement as a whole would be a using-declaration instead of the desired using-directive.  "using class", "using struct", "using union" and "using enum" are currently illegal because "using typename" is used instead for this purpose.

Also, with dependent types, wouldn't you end up with weird wording like "using enum typename A<T>::E;"?  I suppose that the "using enum" and "using class" directives could assume that their parameter is a type and not need typename.

Matthew Woehlke

unread,
Oct 31, 2014, 5:44:56 PM10/31/14
to std-dis...@isocpp.org
On 2014-10-31 16:57, Myriachan wrote:
> My issue with "using enum Enum", "using class Class", etc. is that a
> traditional interpretation would be that the enum/struct/class/union
> keyword is being used as an elaborated type rather than a special
> statement.

Hrmm... okay, I see.

That said... I look at this as there being two forms of "using"...
"using <...> = <...>" defines an alias. "using <something>" says 'bring
<something> into the current scope'. By that dichotomy, the difference
between a using-directive and using-declaration is the presence or
absence of '='.

> Also, with dependent types, wouldn't you end up with weird wording like "using
> enum typename A<T>::E;"?

Possibly... although the above doesn't bother me particularly. (Doesn't
"using namespace" have the same issue? Or do you mean vs. "inline
enum"... but that isn't even applicable to the above example, is it?)

> I suppose that the "using enum" and "using class"
> directives could assume that their parameter is a type and not need
> typename.

I'm good with either one.

--
Matthew

inkwizyt...@gmail.com

unread,
Nov 5, 2014, 3:59:09 PM11/5/14
to std-dis...@isocpp.org
I think this is good place where usage of macro could be helpful:

#define MACRO_ENUM_DEF(X, Y) X,
#define MACRO_ENUM_REF(X, Y) constexpr Y X = Y::X;

#define MACRO_ENUM_TO_STRING(X, Y) case Y::X: return #Y "::" #X;


#define MACRO_DATA_OF_ENUM(FUNC, NAME) \
    FUNC
(A, NAME) \
    FUNC
(B, NAME) \
    FUNC
(C, NAME) \
    FUNC
(D, NAME) \

    FUNC
(E, NAME) \

    FUNC
(F, NAME) \

    FUNC
(G, NAME) \

    FUNC
(I, NAME) \

    FUNC
(NAME##_last, NAME)

enum EnumName
{
MACRO_DATA_OF_ENUM
(MACRO_ENUM_DEF, EnumName)
};
MACRO_DATA_OF_ENUM
(MACRO_ENUM_REF, EnumName)

inline std::string to_string(
EnumName v)
{
    switch(v)
    {

    MACRO_DATA_OF_ENUM(MACRO_ENUM_TO_STRING, EnumName)
    }
    return "";
}

#undef MACRO_ENUM_DEF

#undef
MACRO_ENUM_REF
#undef
MACRO_ENUM_TO_STRING

#undef
MACRO_DATA_OF_ENUM
It's very local, this is transparent to rest of code. Names of macros can be very long because they are used only once.
We get too for free `to_string` (and similar ones too) function. This will be very useful if you want have lot of values in enum (eg. all keys from keyboard).



On Thursday, January 2, 2014 6:42:15 PM UTC+1, Filip Roséen wrote:
You'd need to repeat all the names previously declared in the enumerator-list, if this counts as a violation of Don't Repeat Yourself.. I guess you are out of luck.

enum class E { A, B, C };

...

using E::A;
using E::B;
using E::C;

E x = A;


The above results in a compile-error when using `gcc` (`clang` accepts it), but it is valid according to the standard.
On Thu, Jan 2, 2014 at 6:05 PM, Matthew Woehlke <mw_t...@users.sourceforge.net> wrote:
Is there any way (that doesn't violate DRY) to make the values of a scoped enum (i.e. 'enum class') accessible to the parent scope, as with a traditional enum?

If not, would a method to do so be a reasonable addition to the standard?


--
Matthew

--

--- You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.

ajay kumar

unread,
Oct 4, 2015, 7:15:25 AM10/4/15
to ISO C++ Standard - Discussion, mw_t...@users.sourceforge.net
Reply all
Reply to author
Forward
0 new messages