A constexpr function can be used in non-constant-expression context as shown below.

constexpr int min(int a, int b)
{
    return a < b ? a : b;
}
 
constexpr int result{min(1, 2)};
int value{min(3, 2)};

So, it can be used in both places. This is very useful as we do not need to create double functions for the same purpose. Now let’s take a look at another example where constexpr function calls normal function.

int getMax(int a, int b)
{
    return a < b ? b : a;
}
 
constexpr int max(int a, int b)
{
    return getMax(a, b);
}
 
int value{max(1, 2)}; // works
constexpr int result{max(1, 2)}; // compiler fails

If we compile this program with C++ standard prior to C++23, it would fail. In C++23 prior versions, this is an ill-formed program because there is no set of arguments that will make max() evaluated at compile-time. If we add this condition as shown below, it compiles successfully. However, we still can’t use this function in constant expression context.

constexpr int max(int a, int b)
{
    if (a < 0) // we can put any condition
        return 0;
    return getMax(a, b);
}

With this we have some set of arguments (where a is negative) where this function can be evaluated at compile time and compiler can successfully compile this function.

int value{max(1, 2)}; // works
constexpr int result{max(1, 2)}; // compiler fails

This restriction was revoked in C++23 (P2448R1), so we can remove that condition and compiler will not complain about that function.

How this is useful? Well, it allows us to use constexpr function in non-constant expression context. It will still fail for constant expression context, which is right.

References

  1. https://www.learncpp.com/cpp-tutorial/constexpr-functions-part-2/