Consider following code snippet with C++20 using-enum-declaration:
namespace A { enum A {}; };
using namespace A;
using enum A;
gcc-trunk rejects it with:
<source>:4:12: error: reference to 'A' is ambiguous
4 | using enum A;
| ^
<source>:1:20: note: candidates are: 'enum A::A'
1 | namespace A { enum A {}; };
| ^
<source>:1:11: note: 'namespace A { }'
1 | namespace A { enum A {}; };
| ^
<source>:4:12: error: 'A' has not been declared
4 | using enum A;
| ^
However, msvc accepts it. Interestingly, if I add a namespace qualifier for enum A
:
namespace A { enum A {}; };
using namespace A;
using enum A::A;
gcc accepts it this time, but msvc rejects it with:
<source>(4): error C2872: 'A': ambiguous symbol
<source>(1): note: could be 'A'
<source>(1): note: or 'A::A'
Which compiler is right?
Following is an example of enum declaration. // The name of enumeration is "flag" and the constant // are the values of the flag. By default, the values // of the constants are as follows: // constant1 = 0, constant2 = 1, constant3 = 2 and // so on. enum flag {constant1, constant2, constant3, ....... };
Check out our Data Structures in C course to start learning today. The keyword ‘enum’ is used to declare new enumeration types in C and C++. Following is an example of enum declaration. // The name of enumeration is "flag" and the constant // are the values of the flag.
It is mainly used to assign names to integral constants, the names make a program easy to read and maintain. The keyword ‘enum’ is used to declare new enumeration types in C and C++. Following is an example of enum declaration. // The name of enumeration is "flag" and the constant // are the values of the flag.
The value assigned to enum names must be some integeral constant, i.e., the value must be in range from minimum possible integer value to maximum possible integer value. 5. All enum constants must be unique in their scope. For example, the following program fails in compilation.
gcc is wrong here (submitted 100'084).
The grammar for using enum A;
is from [enum.udecl]:
using-enum-declaration:
using
elaborated-enum-specifier;
Lookup for such a thing is defined in [basic.lookup.elab]:
If the class-key or
enum
keyword in an elaborated-type-specifier is followed by an identifier that is not followed by ::
, lookup for the identifier is type-only ([basic.lookup.general]).
An elaborated-enum-specifier is one kind of elaborated-type-specifier, so we do type-only lookup. Which is defined as, in [basic.lookup.general]/4:
However, if a lookup is type-only, only declarations of types and templates whose specializations are types are considered; furthermore, if declarations of a typedef-name and of the type to which it refers are found, the declaration of the typedef-name is discarded instead of the type declaration.
This means that when we look up A
, while we find both the enum A
and the namespace A
, because our lookup is type-only we only consider the former and not the latter. As a result, we only have a single candidate and that's the one our lookup finds. There is no ambiguity.
MSVC is correct here. The first case is a type-only lookup (as it’s considered an elaborated type specifier), so it ignores the namespace and finds the enumeration via the using-directive. In the second case, the following ::
allows namespaces to be found, so it’s ambiguous.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With