c++c++20constexprc++23requires-expression

Can you call a static constexpr member function at compile time?


g++ and clang++ disagree on whether I can call a static constexpr method at compile time. Here is a small program to illustrate the difference:

#include <print>

struct foo {
  static constexpr int max_size() { return 0; }
};

struct bar {
};

void
report(const char *name, const auto &&x)
{
  if constexpr (requires { requires x.max_size() < 5; })
    std::println("{} is okay", name);
  else
    std::println("{} is not okay", name);
}

int
main()
{
  report("foo", foo{});
  report("bar", bar{});
}

// Clang 18.1.8
// clang++ -std=c++23 -O2 -Wall -Werror constexpr.cc -o constexpr
// foo is not okay
// bar is not okay

// g++ 14.1.1
// g++ -std=c++23 -O2 -Wall -Werror constexpr.cc -o constexpr
// foo is okay
// bar is not okay

Which compiler is right?

As a workaround for this particular case, if I say decltype(auto(x))::max_size() < 5 both compilers behave like gcc in the above example.


Solution

  • This relates to P2280: Using unknown pointers and references in constant expressions.

    Originally the rule was that the expression x.max_size() < 5 has to at least evaluate x, and given that it's a reference whose origin you don't know - that's unusable in constant expressions. Now, the rule is that x is treated as some "reference to unknown object" of its type -- which might still be unusable in constant expressions (i.e. if you read some non-static data member)... but might work just fine if all you need with it is the actual type. The paper has more details.

    And in your example, since foo::max_size() is static, the reference-to-unknown is sufficient for use in constants, so as of the adoption of P2280, the example is valid.

    gcc implements P2280 as of gcc 14, clang doesn't yet (as you can see in this table), hence the implementation divergence. gcc has the correct behavior going forward.