Given a C++11 enum class, nested inside several long- and ugly-named namespaces:
namespace
long_and_ugly
{
enum class
colour
{
red,
green,
blue
};
}
Can aliases be made of the enumeration values? With clang++ 3.5, it is possible to do what follows:
using long_and_ugly::colour; // take all the values into the current namespace
using long_and_ugly::colour::red; // take only 'red' into the current namespace
function_taking_colour_argument( red ); // instead of fully referring to the value
g++ 4.9, however, complains. I can't copy its error message because I can't access the code, but it explicitly complained about the usage of the using directive or declaration. I have also tried this:
using red = long_and_ugly::colour::red;
But it also failed. I'm sorry for not pasting the errors. Nevertheless, I believe you should be able to reproduce it.
Question(s)
Is it possible to declare aliases to enumeration values in standard C++11, or was I using a clang extension?
If it is, what is the correct syntax?
Enumerators in using-declarations
The problem is that the standard says that you shall not refer to an enumerator inside an enum class when using specifying a using-declaration.
7.3.3p7 The using declaration [namespace.udecl] (n3337)
A using-declaration shall not name a scoped enumerator.
namespace N {
enum class E { A };
}
using N::E; // legal
using N::E::A; // ill-formed, violation of [namespace.udecl]p7
Note: clang does accept both lines above; here's a relevant bug report.
It's perfectly fine to refer to the actual name of the enum class itself, but trying to refer to one of its enumerators is ill-formed.
Enumerators in alias-declarations
The standard says that an alias-declaration can only be used to refer to a type-name, since an enumerator isn't a type, using one in such context is ill-formed.
namespace N {
enum class E { A };
}
using x = N::E; // legal, `N::E` is a type
using y = N::E::A; // ill-formed, `N::E::A` isn't a type
Alternatives to using- and alias-declarations
You could declare a constant having whatever-name-of-your-choice initialized with the value you'd like to "alias":
namespace N {
enum class E { A };
}
constexpr N::E x = N::E::A;
int main () {
N::E value = x; // semantically equivalent of `value = N::E::A`
}
Sort of:
namespace long_and_ugly {
enum class colour
{
red,
green,
blue
};
}
const colour red = long_and_ugly::colour::red;
Related
In this answer it was mentioned that in the upcoming C++20 standard it is possible to use the using statement on enum class and import the enum fields into the local namespace.
I was wondering if that also means that I can also use it within class definitions like this:
class Foo {
enum class Color
{
red,
blue
};
using enum Color;
};
int main()
{
Foo::Color c = Foo::red;
}
Or do I still need to give the full namespace?:
Foo::Color c = Foo::Color::red;
I tried it in wandbox.org, but it seems that neither gcc nor clang know about using enum yet.
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...)
The C++14 (precisely, N4296) says with respect to enumerations, in 7.2:11:
Each enum-name and each unscoped enumerator is declared in the scope
that immediately contains the enum-specifier.
Now what happens if a namespace N contains an opaque-enum-declaration of an enum E, and later the enumeration is fully declared from the global namespace? Shall we find its enumerators in the global namespace, or in the namespace N?
Of course, in order to opaque-declare an unscoped enumeration, it shall have a fixed underlying type. Consider the following piece of code.
namespace N { enum E : int; }
enum N::E : int {A,B};
namespace N {
int foo() {
return int(::N::B);
}
}
int bar() {
//return int(::A);
return int(A);
}
The first line in bar is commented out, because clang++ -std=c++14 says:
no member named 'A' in the global namespace; did you mean simply 'A'?
Gcc fails to compile both lines in bar(). So both gcc and clang declare the enumerations in the namespace N.
So my questions are:
What is the scope that immediatelly contains the enum specifier? (I thing it is the scope of the global namespace).
Should the enumerators A, B be defined in the global namespace?
In the bar function, why ::A does not refer to the enumerator, but simple A does?
Why the expression ::N::B in the function N::foo denotes the enumerator?
EDIT 1: the original declaration was enum ::N::E : int {A,B};, but gcc was not able to parse it (bug report), so I removed the leading colons to use enum N::E : int {A,B};
EDIT 2: the clang's behavior is a bug
The enumeration E is declared within the namespace N, even though its definition is set within the global namespace. As such, it can only be accessed in the scope of N.
The bar function should then be defined as:
int bar() {
return int(N::A);
//SAME AS --> return int(::N::A);
}
I notice that the following code compiles with recent compilers:
int main()
{
int x;
struct x;
x = 210; // ←
}
As I recall it didn't compile some years ago.
Were the lookup rules changed in C++11 or C++14 to make this code “work” (thus breaking use of struct variable_name; as a means to ensure no use of the variable in the following code)?
Update:
Evidently I remembered incorrectly. I have verified that the code compiled OK even with Visual C++ 2010. However, when used for parameters the struct name is in an inner scope, and shadows, like in this code:
void foo( int x )
{
struct x;
x = 210; // ← Error
}
int main()
{
}
Accordingly I have selected as “solution” the answer that there was no change; the rules were always like this.
[basic.scope.hiding]/2 A class name (9.1) or enumeration name (7.2) can be hidden by the name of a variable, data member, function, or enumerator declared in the same scope. If a class or enumeration name and a variable, data member, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the variable, data member, function, or enumerator name is visible.
This language has existed since C++98. If you've seen a compiler that worked differently, that compiler was pre-standard, or just plain buggy.
What you did was forward declarations of struct x. You did not declared and new variable called x. Example:
struct foo;
int foo;
struct foo {
int foo;
};
struct foo thisisfoovariable;
Above are declarations of only two variables: foo (of type int) and thisisfoovariable, which type is struct foo.
I am trying to use a std::bitset with an enum but I am getting a compilation error saying
template argument 1 is invalid
Funny thing is that when I use any of the enumerated value without the enumeration scope it works fine.
Do you know why?
Below the code
enum MyTypes {
Alpha = 1,
Beta = 2,
Gamma = 3
};
std::bitset<MyTypes::Alpha> bitset_wrong; // It doesn't compile.
std::bitset<Alpha > bitset_good; // It works.
It seems that you have an old compiler that does not support specifying qualified names with unscoped enumerators.
Update your compiler.:)
The code you showed is a valid code according to the C++ 2011 Standard.
Here is a quote from the C++ Standard with an example (7.2 Enumeration declarations)
11 Each enum-name and each unscoped enumerator is declared in the
scope that immediately contains the enum-specifier. Each scoped
enumerator is declared in the scope of the enumeration. These names
obey the scope rules defined for all names in (3.3) and (3.4).
[ Example:
enum direction { left=’l’, right=’r’ };
void g() {
direction d; // OK
d = left; // OK
d = direction::right; // OK
}
This code compiles (and appears to work) with both GCC and Clang:
#include <iostream>
struct Foo {
enum { number = 42 };
};
int main()
{
Foo bar;
std::cout << bar.number << std::endl;
}
See it here.
It was surprising to me that the compiler accepts bar.number; all text books I can find teach to use Foo::number to access the enumerated value.
Is this code valid? Note that GCC gives a weird warning ("variable 'bar' set but not used"), while Clang doesn't complain about it.
It's perfectly valid:
[C++11: 7.2/10]: Each enum-name and each unscoped enumerator is declared in the scope that immediately contains the enum-specifier. Each scoped enumerator is declared in the scope of the enumeration. These names obey the scope rules defined for all names in (3.3) and (3.4). [..] An enumerator declared in class scope can be referred to using the class member access operators (::, . (dot) and -> (arrow)), see 5.2.5.
The same text (minus rules for scoped enums) can be found in the C++03 standard, at the same location.
I have to admit, this surprises me somewhat as well. I'd have expected :: to be the only valid mechanism.
The warning you get in GCC is not about the use of the enum, but about the fact that you use nothing but the enum. In such a case, you'd typically write Foo::number and avoid instantiating a Foo instance.
Just a guess here:
Since the enum is technically a type, Foo::number could be treated as a const or static const by the compiler. What would happen if you:
#include <iostream>
using namespace std;
enum { number = 42 };
int main()
{
std::cout << number << endl;
}
In this case, number appears to have been resolved to an immediate, and is valid within both this-> and type. scopes.
As I said, this is just a guess. #LightnessRacesInOrbit has spec quotes. He/She/Insanely-well-informed Turing Machine wins.