This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.

4316. {can_}substitute specification is ill-formed

Section: 21.4.13 [meta.reflection.substitute] Status: WP Submitter: Matthias Wippich Opened: 2025-08-15 Last modified: 2025-11-11

Priority: 1

View all issues with WP status.

Discussion:

Addresses US 114-175

can_substitute and substitute are currently specified in terms of splices in a template argument list:

21.4.13 [meta.reflection.substitute] p3:

Returns: true if Z<[:Args:]...> is a valid template-id (13.3 [temp.names]) that does not name a function whose type contains an undeduced placeholder type. Otherwise, false.

21.4.13 [meta.reflection.substitute] p7:

Returns: ^^Z<[:Args:]...>.

This wording was introduced in P2996R11. However, merging in changes from P3687 "Final Adjustments to C++26 Reflection" in P2996R13 changed the rules for splices in this context. This makes can_substitute and substitute as specified currently ill-formed. We cannot use the given syntax to splice an arbitrary choice of values, types and templates anymore.

While the intent seems clear, this should be rephrased to be more technically correct.

[2025-10-22; Reflector poll.]

Set priority to 1 after reflector poll.

[2025-10-27; Tomasz provides wording.]

Previous resolution [SUPERSEDED]:

This wording is relative to N5014.

  1. Modify 21.4.13 [meta.reflection.substitute] as indicated:

    -1- For value x of type info, and prvalue constant expression X that computes the reflection held by x, let TARG-SPLICE(x) be:

    • -1.1- template [: X :] if is_template(x) is true, otherwise
    • -1.2- typename [: X :] if is_type(x) is true, otherwise
    • -1.3- ([: X :])

    template<reflection_range R = initializer_list<info>>
      consteval bool can_substitute(info templ, R&& arguments);
    

    -1- Let Z be the template represented by templ and let Args... be a sequence of prvalue constant expressions that compute the reflections held by the elements of arguments, in order. Let n be the number of elements in arguments, and ei be the ith element of arguments.

    -2- Returns: true if Z<[:Args:]...TARG-SPLICE(e0), ..., TARG-SPLICE(en-1)> is a valid template-id (13.3 [temp.names]) that does not name a function whose type contains an undeduced placeholder type. Otherwise, false.

    -3- Throws: meta::exception unless templ represents a template, and every reflection in arguments represents a construct usable as a template argument (13.4 [temp.arg]).

    -4- [Note: If forming Z<[:Args:]...TARG-SPLICE(e0), ..., TARG-SPLICE(en-1)> leads to a failure outside of the immediate context, the program is ill-formed. — end note]

    template<reflection_range R = initializer_list<info>>
      consteval info substitute(info templ, R&& arguments);
    

    -5- Let Z be the template represented by templ and let Args... be a sequence of prvalue constant expressions that compute the reflections held by the elements of arguments, in order. Let n be the number of elements in arguments, and ei be the ith element of arguments.

    -6- Returns: ^^Z<[:Args:]...TARG-SPLICE(e0) ..., TARG-SPLICE(en-1)>.

    -7- Throws: meta::exception unless can_substitute(templ, arguments) is true.

    -8- [Note: If forming Z<[:Args:]...TARG-SPLICE(e0), ..., TARG-SPLICE(en-1)> leads to a failure outside of the immediate context, the program is ill-formed. — end note]

[2025-10-27; Reflector comments.]

We lost definition of Z. Use TARG-SPLICE([:Args:])....

[2025-11-03; Tomasz provides wording.]

[Kona 2025-11-05; approved by LWG. Status changed: New → Immediate.]

[Kona 2025-11-08; Status changed: Immediate → WP.]

Proposed resolution:

This wording is relative to N5014.

  1. Modify 21.4.13 [meta.reflection.substitute] as indicated:

    -1- Let TARG-SPLICE(x) be:

    • -1.1- template [: x :] if is_template(x) is true, otherwise
    • -1.2- typename [: x :] if is_type(x) is true, otherwise
    • -1.3- ([: x :])

    template<reflection_range R = initializer_list<info>>
      consteval bool can_substitute(info templ, R&& arguments);
    

    -1- Let Z be the template represented by templ and let Args... be a sequence of prvalue constant expressions that compute the reflections held by the elements of arguments, in order.

    -2- Returns: true if Z<TARG-SPLICE([:Args:])...> is a valid template-id (13.3 [temp.names]) that does not name a function whose type contains an undeduced placeholder type. Otherwise, false.

    -3- Throws: meta::exception unless templ represents a template, and every reflection in arguments represents a construct usable as a template argument (13.4 [temp.arg]).

    -4- [Note: If forming Z<TARG-SPLICE([:Args:])...> leads to a failure outside of the immediate context, the program is ill-formed. — end note]

    template<reflection_range R = initializer_list<info>>
      consteval info substitute(info templ, R&& arguments);
    

    -5- Let Z be the template represented by templ and let Args... be a sequence of prvalue constant expressions that compute the reflections held by the elements of arguments, in order.

    -6- Returns: Z<TARG-SPLICE([:Args:])...>.

    -7- Throws: meta::exception unless can_substitute(templ, arguments) is true.

    -8- [Note: If forming Z<TARG-SPLICE([:Args:])...> leads to a failure outside of the immediate context, the program is ill-formed. — end note]