2

I am trying to use an int member of a constexpr object as a template parameter. The idea is that I create a constexpr Symbol and then have that converted into a SymbolRef<1> (in the real code that 1 is different for different objects).

template<int Id>
struct SymbolRef { };

struct Symbol {
  int const id_;

  consteval Symbol() : id_(1) { }

  template<int Id>
  consteval operator SymbolRef<Id>() const
  {
    return SymbolRef<id_>{};
  }
};

However, trying to compile the above code I get the error:

error: non-type template argument is not a constant expression
   12 |     return SymbolRef<id_>{};
      |                      ^~~
<source>:12:22: note: implicit use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function

The phrase "is only allowed within the evaluation of a call to a 'constexpr' member function" seems to not apply: this is a consteval function!?

I understand that this code is ill-formed when operator SymbolRef<Id>() would be a constexpr member function (in spite of the error message!) because in that case the function must also compile without the constexpr, in which case this->id_ can't be used as a template parameter.

Why is this code illegal when using it with consteval?

EDIT:

The following does compile with g++ - but only when using this->id_.

template<int Id>
struct SymbolRef { };

struct Symbol {
  int const id_;

  consteval Symbol() : id_(1) { }

  template<int Id>
  consteval operator SymbolRef<Id>() const
  {
    return SymbolRef<this->id_>{};
  }
};

I am guessing it is a compiler bug therefore? It seems that the correct behavior is that it would compile with or without this-> and clang++ is just wrong.

3
  • paramer/member are not constexpr, even in consteval fonction/class. Commented Mar 11, 2024 at 18:10
  • It only compiles on gcc if you don't try to use the operator: godbolt.org/z/c5xsaEc9P (Checking errors in templates is best-effort, GCC doesn't have to diagnose it, but clang is "smarter" here to reject it) Commented Mar 11, 2024 at 18:40
  • Also this question might help: stackoverflow.com/q/56130792 (this is just a function parameter) Commented Mar 11, 2024 at 19:43

1 Answer 1

2

consteval/constexpr is not relevant here.

A non-type template argument, regardless of where it appears, must itself be a constant expression.

And the requirement that the compiler error is stating is a bit incomplete. [expr.const]/5.1 says:

An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:

  • [...]
  • this, except in a constexpr function that is being evaluated as part of E;
  • [...]

id_ in the template argument is implicitly interpreted as this->id_ because id_ names a member of the class. This this does appear inside a constexpr function, but not a constexpr function that has been invoked during the constant expression evaluation under consideration.

To see why this can't work, consider that the compiler has to determine the type of every expression already when the template is instantiated, before there is any way to know what what value of id_ the function might be called. Should the type be different for every call to the same function specialization? That's not possible in a statically-typed language.


Note that there will generally only be an error if you actually try to instantiate a specialization of the member function.

However, the compiler is free to decide to fail early if a non-dependent construct would always cause any instantiation to fail. (Technically such a program is IFNDR: ill-formed, no diagnostic required.)

Sign up to request clarification or add additional context in comments.

11 Comments

This is still a bit over my head... it seems to me that it would be possible to instantiate the member function at the moment it is called, because it is called on a constexpr Symbol, with known id_ value at the time of compilation. You CAN do: constexpr Symbol x; Foo<x.id_> foo; assuming Foo<int> exists (or static_assert(x.id_ == 1, "!!");).
@CarloWood Yes, but in constexpr Symbol x; Foo<x.id_> foo; the type of foo is always the same and can be determined immediately when it is being parsed. constexpr doesn't affect anything about how syntax in functions works: Every expression in a constexpr or consteval function has a specific type that can be identified from the function's definition wherever it is placed (or instantiated). A function can't have an expression which sometime has one type and sometimes another. It is just completely contrary to how the language works.
@CarloWood You can have a function template with each specialization of it having a different type for the same expression, but each specialization of such a template is itself a unique function. You can achieve this by making id_ a template parameter of the function instead and passing it in wherever the function is called, similar to what you showed in your previous comment.
@CarloWood Or stated in a different way: In C++ types are always resolved first, while parsing the definition or during template instantiation from a definition. Constant expression evaluation always happens afterwards in a separate step and doesn't differ from runtime evaluation in any way other than that the result may under certain circumstances be used in some places that require a constant expression and that there are constructs like if consteval to intentionally cause divergence during the evaluation.
Yes, but this->id_ does always have the same value in the template function template<int Id> consteval operator SymbolRef<Id>() const. That is the whole idea, that I generate a new function for every (different) value of id_. You said "A function can't have an expression which sometime has one type and sometimes another." but that isn't the case here :/
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.