1. Revision History
1.1. Revision 1 - November 4th, 2023
-
Update wording as per suggestions (thanks, Jens Maurer).
-
Clarify existing practice.
1.2. Revision 0 - August 5th, 2023
-
Initial Release! 🎉
2. Introduction and Motivation
In the oldest proposals for
, the proposal authors noted how the construct would produce a reference under common situations involving declarations and usage of C code inside of it. This was part of a sincere argument to leave
alone as a keyword and instead pivot to the
keyword instead, with some authors directly pointing out serious flaws with trying to take the
keyword from the existing vendor extension and C implementation space [n2343] [n1978].
The intention was that keyword differed enough from the existing practice from C and C++ compilers with the
and
extensions that they should leave it alone, and let C standardize it at its own pace. C23 added
and, at the request of users working on the Linux Kernel and within the GCC bug tracker,
to solve the distinct issues with C [n2927]. Furthermore, it keeps with the existing practice that allows both expressions and type names to go into the
operator in a way that
chose not to.
Critically, this means that
and
cannot be fully approximated with
alone, or even a macro such as
. C and C++ implementations the
extension can already take a type specifier (or, more specifically for the case of C++ grammar, a type-id). This is exceptionally important for
usage in macros: often times, rather than inventing an expression with which to provide the right type to a macro, it can be expedient to just pass a type in directly where possible to control the eventual cast or type inside of particularly complex macros. This is done a handful of times in C world. This is not as important for C++, but (some) macros tend to live in a shared world for C and C++.
Some thought was given whether for this proposal to allow
in the grammar as a type-based pass-through. An earlier revisions of [n1978] (N1607, to be precise) states, it was left untouched so to let it potentially be used as a "meta"-type value. That paper even casually references the idea of doing
. We believe it is highly unlikely this will ever happen given the direction reflection has gone in for its facilities (e.g. preferring a sigil/operator or the keyword
). Furthermore, the overall direction of C++ with
has eschewed the idea that there would ever be a used for
as a
-producing expression. It could be safe to take
for C++ to just act as a placeholder for
to harmonize its version of
with C’s, even if it might not provide any essential value.
But, we do NOT propose it here and consider it outside the scope of this proposal as there is no existing practice for
.
This proposal adds
and
to C++ to harmonize the 2 languages and close the loophole left from 20 years ago (for C++) and 30 years ago (from C), now that C has finally standardized its long-implemented keyword extensions.
2.1. What About Typical Language Differences for C and C++ with typeof
& friends?
The behavior of
is intended to be exactly identical to
, just without references. This includes allowing bitfields and similar C++-isms capable from
. This follows existing practice, modulo bugs in GCC around the name of destructors (https://godbolt.org/z/v1W5hGdYz).
3. Specification
The specification is relative to the latest C++ Working Draft, [n4950].
3.1. Language Wording
3.1.1. Add to Table 5: Keywords ([ tab : lex . key ]
) 2 new keywords
5.11 Keywords [lex.key]…
alignas constinit false public true alignof const_cast float register try asm continue for reinterpret_cast typedef auto co_await friend requires typeid bool co_return goto return typename break co_yield if short typeof case decltype inline signed typeof_unqual catch default int sizeof … char delete long static char8_t do mutable static_assert char16_t double namespace static_cast char32_t dynamic_cast new struct class else noexcept switch concept enum nullptr template const explicit operator this consteval export private thread_local constexpr extern protected throw
3.1.2. Modify "Decltype specifiers" [dcl.type.decltype]
9.2.9.5 Decltype specifiers [dcl.type.decltype]decltype-specifier:
decltype expression
(
)
typeof expression
(
)
typeof type-id
(
)
typeof_unqual expression
(
)
typeof_unqual type-id
(
) For an
E, the type denoted by
expression is defined as follows:
decltype ( E )
- if E is an unparenthesized id-expression naming a structured binding ([dcl.struct.bind]),
is the referenced type as given in the specification of the structured binding declaration;
decltype ( E ) - otherwise, if E is an unparenthesized id-expression naming a non-type template-parameter ([temp.param]),
is the type of the template-parameter after performing any necessary type deduction ([dcl.spec.auto], [dcl.type.class.deduct]);
decltype ( E ) - otherwise, if E is an unparenthesized id-expression or an unparenthesized class member access ([expr.ref]),
is the type of the entity named by E. If there is no such entity, the program is ill-formed;
decltype ( E ) - otherwise, if E is an xvalue,
is
decltype ( E ) , where
T && is the type of E;
T - otherwise, if E is an lvalue,
is
decltype ( E ) , where
T & is the type of E;
T - otherwise,
is the type of E.
decltype ( E ) The operand of the
,
decltype , or
typeof specifier is an unevaluated operand.
typeof_unqual …For an expression E, the type denoted by
is the type of
typeof ( E ) . The type denoted by
E is formed by removing top-level cv-qualifiers from the type denoted by
typeof_unqual ( E ) .
typeof ( E ) For a type-id
, the type denoted by
T is formed by removing top-level reference qualifiers, if any, from
typeof ( T ) . The type denoted by
T is formed by removing top-level cv-qualifiers and then reference qualifiers from
typeof_unqual ( T ) .
T
3.1.3. Modify decltype
mention in Template Deduction’s "General" Clause [temp.deduct.general] to include typeof
and typeof_unqual
13.10.3.1 General [temp.deduct.general]…The deduction substitution loci are
- the function type outside of the noexcept-specifier,
- the explicit-specifier, and
- the template parameter declarations.
The substitution occurs in all types and expressions that are used in the deduction substitution loci. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside
,
sizeof ,
decltype ,
typeof , and other contexts that allow non-constant expressions. The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered. If substitution into different declarations of the same function template would cause template instantiations to occur in a different order or not at all, the program is ill-formed; no diagnostic required.
typeof_unqual …
3.1.4. Modify decltype
mention in Template’s "Type equivalence" Clause [temp.type] to include typeof
and typeof_unqual
13.6 Type equivalence [temp.type]…If an expression e is type-dependent,
,
decltype ( e ) , or
typeof_unqual ( e )
typeof ( e ) denotesdenote a unique dependent type. Two such decltype-specifiers refer to the same type only if their expressions are equivalent ([temp.over.link]).…
3.1.5. Modify decltype
mentions in Template’s "Dependent types" Clause [temp.dep.type] to include typeof
and typeof_unqual
13.8.3.2 Dependent types [temp.dep.type]…A type is dependent if it is
- a template parameter,
- denoted by a dependent (qualified) name,
- a nested class or enumeration that is a direct member of a class that is the current instantiation,
- a cv-qualified type where the cv-unqualified type is dependent,
- a compound type constructed from any dependent type,
- an array type whose element type is dependent or whose bound (if any) is value-dependent,
- a function type whose parameters include one or more function parameter packs,
- a function type whose exception specification is value-dependent,
- denoted by a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent or is a pack expansion,
or- denoted by
,
decltype ( expression ) , or
typeof ( expression ) , where expression is type-dependent
typeof_unqual ( expression ) ., or- denoted by
or
typeof ( type - id ) , where type-id is type-dependent.
typeof_unqual ( type - id ) …
3.1.6. Modify decltype
mentions in Template’s "Dependent types" Clause [temp.dep.type] to include typeof
and typeof_unqual
13.10.3.6 Deducing template arguments from a type [temp.deduct.type]…The non-deduced contexts are:
- The nested-name-specifier of a type that was specified using a qualified-id.
- The expression or type-id of a decltype-specifier.
- …
…