Skip to content

CWG3111 [temp.param] Correctly handle template parameter objects of array type #791

Description

@katzdm

Full name of submitter: Dan Katz

Reference (section label): [temp.param], [meta.reflection.array], [meta.reflection.queries]

Issue description
The specification for std::meta::reflect_constant_array includes the following clause ([meta.reflection.array]/11.2):

  • If sizeof...(V) > 0 is true, then the template parameter object ([temp.param]) of type const T[​sizeof...(​V)] initialized with {[:V:][.](https://eel.is/c++draft/meta.reflection.array#11.1.sentence-1)..}.

However, the paragraph that introduces "template parameter objects" ([temp.param]/13) does not make it clear that such objects can have array type:

An id-expression naming a constant template parameter of class type T denotes a static storage duration object of type const T, known as a template parameter object, which is template-argument-equivalent ([temp.type]) to the corresponding template argument after it has been converted to the type of the template parameter ([temp.arg.nontype]). No two template parameter objects are template-argument-equivalent.

This paragraph only seems to succeed in establishing that there are template parameter objects of class type. Indeed, there is good reason to think that there are no template parameter objects of array type, since [temp.param]/15 tells us that template arguments of array type undergo array-to-pointer conversion:

A constant template parameter of type "array of T" or of function type T is adjusted to be of type "pointer to T".

This same conversion is currently specified to occur when std::meta::constant_of is applied to a reflection that represents a variable of array type. However, that behavior changed during CWG review between P2996R11 (link) and P2996R12 (link) as we began to couple the function's behavior more closely to template parameter objects. As a consequence, the following no longer holds:

constexpr int is[] = {1, 2, 3};
constexpr std::meta::info r = std::meta::reflect_constant_array(is);
static_assert(std::meta::constant_of(^^is) == r);  // error: 'constant_of(^^is)' is a reflection of a pointer value.

I'd like to propose the following changes to address both concerns:

  1. Modify [temp.param]/13 to more clearly allow for template parameter objects of array type.
  2. Add a note after [temp.param]/13 clarifying that there can be template parameter objects of array type.
  3. Modify [meta.reflection.queries]/11.2 to define constant_of in terms of reflect_constant_array instead of reflect_constant when the argument represents a variable or object having array type.

Suggested resolution
Modify [temp.param]/13 as follows:

Template parameter objects are static storage duration objects of non-volatile const type with the property that for every such object O, any template parameter object that is template-argument-equivalent ([temp.type]) to O is necessarily O. An id-expression naming a constant template parameter of class type T denotes a static storage duration the template parameter object of type const T, known as a template parameter object, which is template-argument-equivalent ([temp.type]) to the corresponding template argument after it has been converted to the type of the template parameter ([temp.arg.nontype]). No two template parameter objects are template-argument-equivalent.

Add the following note after [temp.param]13:

[ Note: There can be template parameter objects of array type ([meta.reflection.array]), but such objects are never denoted by an id-expression that names a constant template parameter. — end note ]

Modify the specification of [meta.reflection.queries]/7 as follows:

Effects: Equivalent to:

  if constexpr (is_annotation(R)) {
    return C;
+ } else if constexpr (is_array_type(type_of(R)) {
+   return reflect_constant_array([:R:]);
  } else {
    return reflect_constant([:R:]);
  }

Activity

  1. t3nsor commented on Oct 30, 2025

    @t3nsor

    I find the proposed definition of "template parameter object" to be confusing: a definition that uses "with the property that" suggests that to determine whether something is a template parameter object, you check whether it has that property. This property should be in a separate sentence (as it currently is) to make it clear that it's not part of the definition.

  2. katzdm commented on Oct 30, 2025

    @katzdm
    Author

    I find the proposed definition of "template parameter object" to be confusing: a definition that uses "with the property that" suggests that to determine whether something is a template parameter object, you check whether it has that property. This property should be in a separate sentence (as it currently is) to make it clear that it's not part of the definition.

    Yeah, I hear that. I'm trying to separate the definition from the template parameter mechanism, since we evidently now have template parameter objects that are never consequent from an instantiation. On the other hand, their only prior definition was "the static storage duration objects that happen to be consequent from an instantiation." And yet, that property (i.e., unique under template-argument-equivalence) is their most important quality.

    So I think the the confusion here is that "template parameter objects" is more correctly a "family" or "class" of not-very-special static storage duration objects. Here's another try:

    Updated suggestion resolution

    Modify [temp.param]/13 as follows:

    There is a (possibly empty) set of static storage duration objects of non-volatile const type, called template parameter objects, for which no two such objects are template-argument-equivalent ([temp.type]). An id-expression naming a constant template parameter of class type T denotes a static storage duration object of type const T, known as a template parameter object, the template parameter object which is template-argument-equivalent ([temp.type]) to the corresponding template argument after it has been converted to the type of the template parameter ([temp.arg.nontype]). No two template parameter objects are template-argument-equivalent.

    Add the following note after [temp.param]/13:

    [ Note: There can be template parameter objects of array type ([meta.reflection.array]), but such objects are never denoted by an id-expression that names a constant template parameter. — end note ]

    Modify the specification of [meta.reflection.queries]/7 as follows:

    Effects: Equivalent to:

      if constexpr (is_annotation(R)) {
        return C;
    + } else if constexpr (is_array_type(type_of(R)) {
    +   return reflect_constant_array([:R:]);
      } else {
        return reflect_constant([:R:]);
      }
  3. katzdm commented on Oct 31, 2025

    @katzdm
    Author

    Tomasz points out the issue also exists for function-to-pointer conversion:

    void f() {}
    
    constexpr auto *fptr = &f;
    constexpr auto &fref = f;
    
    static_assert(constant_of(^^fptr) == constant_of(^^fref));  // currently holds

    Consider also the following:

    template <auto &F> void tfn() {}
    void f() {}
    
    constexpr auto &fref = f;
    static_assert(constant_of(^^fref) == template_arguments_of(^^tfn<f>)[0]);  // currently fails

    This motivates the following updated change for [meta.reflection.queries]/7:

    Effects: Equivalent to:

      if constexpr (is_annotation(R)) {
        return C;
    + } else if constexpr (is_array_type(type_of(R)) {
    +   return reflect_constant_array([:R:]);
    + } else if constexpr (is_function_type(type_of(R)) {
    +   return reflect_function([:R:]);
      } else {
        return reflect_constant([:R:]);
      }
  4. jensmaurer commented on Nov 2, 2025

    @jensmaurer
    Member
  5. changed the title [-][temp.param] Correctly handle template parameter objects of array type[/-] [+]CWG3111 [temp.param] Correctly handle template parameter objects of array type[/+] on Nov 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions