Point of declaration for an enumeration - c++

What is the point of declaration of enumeration types? Is it immediately after the name of an enumeration? I saw Standard C++ 14 (n4296) §3.3.2/3:
The point of declaration for an enumeration is immediately after the
identifier (if any) in either its enum-specifier (7.2) or its first
opaque-enum-declaration (7.2), whichever comes first
But when I try to reproduce it;
template <class T>
struct CL
{
using UndType = int;
};
enum class E: CL<E>::UndType; //error: E is undefined
I have got an error on all the compilers, although enum-base for enumeration E is placed after the identifier and must be visible.

The following;
enum class E : CL<E>::UndType;
Is not accepted as a valid declaration in some current implementations (tested clang++, g++ and MSVC). They do not accept the, as yet incomplete type E, in the enum-base CL<E>::UndType. The error given in the tested implementations is that E is undeclared at that point. They seem to place the point of declaration at the end of the enum-base, they consider it declared once it is complete.
When reading the specifications;
§14.3.1/2 Template type arguments
[ Note: A template type argument may be an incomplete type (3.9). — end note ]
And
§7.2/6 Enumeration declarations
An enumeration whose underlying type is fixed is an incomplete type from its point of declaration (3.3.2) to immediately after its enum-base (if any), at which point it becomes a complete type.
Does hint at it being compilable; as is the case with CRTP implementations.
I'm note sure if this (i.e. the failure to compile enum class E : CL<E>::UndType;) is the intention or if it was considered as a use case. From the specification, the opaque enum declaration is given some "special" treatment w.r.t. its base type and the requirement that it must be an integral type.
Presumably, the code should be compilable given the resolution to CWG#1482.
As for current workarounds...
This;
enum class E; // default underlying type is int
Is the minimum declaration.
The opaque declaration could be either;
enum class E : int; // int base
The following would be a full definition (including enumerators);
enum class E : int {/*...*/};
Or to use the class template, another type (possibly void) could be used.
enum class E : CL<void>::UndType;

Now CWG2516 is opened for this.
I believe it's a bug in the Standard which forbids a portable implementation of is_scoped_enum.

Related

How does the lookup rule apply to a name that is used in a default member initializer within a class template?

#include <iostream>
template<class T>
struct A{
int c = T::a;
};
struct B{
A<B> cc;
static const int a = 0;
};
int main(){
}
GCC and Clang both accept the above example. Regarding how the lookup rule applies to a name in a class template specialization, which might be a member of a class that causes the implicit instantiation of the class template specialization is not clear in c++20 or before. Fortunately, it's clear in the current draft.
[basic.lookup#class.member.lookup-3]
The declaration set is the result of a single search in the scope of C for N from immediately after the class-specifier of C if P is in a complete-class context of C or from P otherwise. If the resulting declaration set is not empty, the subobject set contains C itself, and calculation is complete.
It seems that the rule can be used to interpret most cases that I said in the above, however, consider the above example, it's a bit special.
The definition of non-static member cc would cause the implicit instantiation of specialization A<B>, at this point, it's not the complete context of class B. Hence, the subsequent lookup for a name in the scope B should be from this point. If T::a is used as the type-specifier of a non-static member of class template A, It's 100% sure that the compiler would report an error that says no identifier a can be found in the scope of B.
However, except for the following rule
temp.res#general-1
If the name is dependent (as specified in [temp.dep]), it is looked up for each specialization (after substitution) because the lookup depends on a template parameter.
I cannot find any wording in the standard that states whether an implicit instantiation of A can cause the lookup rule to be applied to the name within the default member initializer or is not at that point.
At first glance of the above rule, It should mean that the lookup should be performed for the name when the enclosing class template specialization is instantiating, then a cannot be found in the scope of B since it's not deemed as a complete class at that point and only these declarations that are reachable at point P can be found.
Obviously, the behavior of GCC and Clang consider T is a complete type such that a can be found in T. In other words, it seems that these compilers do not immediately perform the lookup for T::a when A is instantiating. If I miss a certain special rule, please point them out.

Does the use a simple-template-id in a nested-name-specifier unambiguously mean a class template specialization?

struct A{
template<typename U>
void T(){}
};
struct B{
template<typename U>
struct T{
using type = U;
};
};
struct C:A,B{
};
int main(){
C::T<int>::type d;
}
This example is accepted by neither GCC nor Clang.
As per basic.lookup.qual#1
The name of a class or namespace member or enumerator can be referred to after the​::​ scope resolution operator ([expr.prim.id.qual]) applied to a nested-name-specifier that denotes its class, namespace, or enumeration. If a​::​ scope resolution operator in a nested-name-specifier is not preceded by a decltype-specifier, lookup of the name preceding that ​::​ considers only namespaces, types, and templates whose specializations are types.
That means that when looking up the declarations for the template name T, the specialization of T shall denote a type in this context. On the other hand, as per class.member.lookup#4
If C contains a declaration of the name f, the declaration set contains every declaration of f declared in C that satisfies the requirements of the language construct in which the lookup occurs.
Again, when looking up the template T in the scope of C, only those templates whose specialization is a type should be considered by this lookup. The scope of C does not have any declarations for T, hence the lookup will be performed for S(T,C) in every one of its base classes. The template T in A does not satisfy the requirement. Meanwhile, the template T declared in the scope of B does satisfy the requirement. So the lookup is not ambiguous and the B::T is the unique result. That means C::T<int>::type d should be well-formed. Why do both GCC and Clang reject this example? Can it be considered a bug of in both? If I missed something, what's the reason that this example should be ill-formed?
The lookahead required to have the lookup for T depend on the :: even as the interpretation of < depends on the meaning of T is considered undesirable. As such, lookup for a name followed by < is not restricted to types and namespaces regardless of any >:: found later. P1787R6 fixed this, restricting the special lookup to identifiers immediately followed by :: (since other kinds of names can’t refer to types or namespaces anyway).

What is the colon in an enum class declaration?

I just found this weird piece of code in the align_val_t definition of Visual Studio 2019's standard library:
namespace std
{
enum class align_val_t : size_t {};
}
What does the colon mean?
enum class align_val_t : size_t {};
// ^
// this thing
All standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS.
The enum Name : UnderlyingType {} syntax defines an enum that is said to be fixed, with an explicitly specified underlying type. From [dcl.enum]/5:
Each enumeration defines a type that is different from all other types. Each enumeration also has an underlying type. The underlying type can be explicitly specified using an enum-base. For a scoped enumeration type, the underlying type is int if it is not explicitly specified. In both of these cases, the underlying type is said to be fixed. [...]
enum class align_val_t : size_t {};
// ^
// this thing
In this particular example, an enum named align_val_t is defined (in the std namespace) as fixed with explicitly specified underlying type size_t (std::size_t, to be precise).
Scoped and unscoped enumerations & underlying types
// Unscoped enumeration; [enum Name {}]
// - underlying type not fixed.
enum UnscopedUnfixed { a, b };
auto uu_a = a;
// Scoped enumeration; [enum class Name {} / enum struct Name {}]
// - underlying type implicitly fixed to int.
enum class ScopedImplicitlyFixed { c, d };
auto sif_c = ScopedImplicitlyFixed::c;
// Scoped enumeration; [enum class Name : TYPE {} / enum struct Name : TYPE {}]
// - underlying type explicitly fixed.
enum class ScopedExplicitlyFixed : unsigned int { e, f };
auto sef_e = ScopedExplicitlyFixed::e;
enum class align_val_t /*HERE----->*/:/*<------HERE*/ size_t {};
That colon is (an optional) part of the syntax of an enum (class) definition. It separates the name of the enum (class) and the underlying type.
Since C++11, this is part of the syntax of enums, for which you can (but don't have to) specify the underlying type.
It's saying that every member of the enum has the type std::size_t; exactly that type, nothing else. In the olden days, it wasn't always quite so clear what the type would be, at least not by simply looking at the code.

Why is decltype(class::class::class::member) valid [duplicate]

This question already has an answer here:
Why are redundant scope qualifications supported by the compiler, and is it legal?
(1 answer)
Closed 6 years ago.
I noticed by accident that this code compiles and works correctly:
struct M { int some_int; };
static_assert(std::is_same<
decltype(M::M::M::M::some_int) /* <- this */,
int>::value, "Types must be int");
Why is this correct (decltype(M::M::M::M::some_int) <=> decltype(M::some_int))?
What other constructs one can use this pattern with class::class::...::member?
Compiler: Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23824.1 for x86
This is valid in all contexts, not just decltype. A class contains its own name as the injected class name. So within a class A::B::M, the name M is injected to refer to the class A::B::M. This means that you can then use M::M::M::some_member to refer to members of that class, if you really want to.
[Live example]
Note that when referring just to the class name itself (e.g. M::M::M), the situation is slightly different. If such a reference occurs in a place where a reference to a function could also potentially be correct, the syntax is taken to refer to the constructor instead. However, in type-only contexts, even such reference is valid. Example:
M::M::M m; // illegal, M::M interpreted as reference to constructor
struct D : public M::M::M // legal, a function could not be references here, so M::M::M means M
{};
This works because of the injected-class-name:
(N3337) [class]/2:A class-name is inserted into the scope in which it is declared immediately after the class-name is seen.
The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name.
For purposes of access checking, the injected-class-name is treated as if it were a public member name. [...]
So you can arbitrarily nest these, and they'll work with derived types as well:
struct A { using type = int; };
struct B : public A {};
using foo = B::B::B::A::A::A::type;
Note that in the case of A[::A]*::A, the injected-class-name can be considered to name the constructor instead:
[class.qual]/2: In a lookup in which the constructor is an acceptable lookup result and the nested-name-specifier nominates
a class C:
— if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name
of C (Clause 9), or
— [...]
the name is instead considered to name the constructor of class C.

explicit template instantiation gives compile error on XLC, but works on other compilers

The following code is a simplified minimal version of a feature I am trying to implement for a client requirement.
It fails to compile on IBM's XLC compiler (version 9 and 11, both) with the error A non-type template parameter cannot have type "int X::*".. However, I have tried the same code with g++ 4.7.2, clang++ 3.2 and Intel-13.0, and they compile it successfully.
I am curious to know if XLC is the only sane voice here, or are the other compilers correct?
struct X {
X() : y(123) {}
int y;
};
struct XFoo {
typedef int X::* Type;
};
template <typename Name, typename Name::Type value>
struct Bar {
typename Name::Type getValue(Name) {
return value;
}
};
template class Bar<XFoo, &X::y>; // xlc error here, works fine on others
int main() {}
I read through the C++ 2003 standard chapter on templates several times, and could not conclusively find something which prohibits using <type> <class>::* as a non-template type. I have already searched through SO and search engines for an explanation, but have not come across any authoritative source which helps me decide.
I understand that this may not be a good coding practice, but this is required for the client code since their requirement is somewhat unique. I have also tried out various other alternatives but it does not work for them.
§ 14.1/4 of the C++03 Standard allows having pointers to member as a template parameter:
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
integral or enumeration type,
pointer to object or pointer to function,
reference to object or reference to function,
pointer to member.
Correspondingly, per § 14.3.2/1:
A template-argument for a non-type, non-template template-parameter shall be one of:
an integral constant-expression of integral or enumeration type; or
the name of a non-type template-parameter; or
the address of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as & id-expression where the & is optional if the name refers to a function or array, or if the corresponding template-parameter is a reference; or
a pointer to member expressed as described in 5.3.1.
Thus, non-type template parameters can be pointers to member. There are some limitations which apply to template specialization, but they don't seem to apply in this case.
Pointer-to-member template parameters are explicitly allowed, XLC should be served a bug report.