Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using throw in a constexpr or a consteval function in order to generate compile-time error

The problem

Using static_assert to generate compile-time error is not always easy because it requires a constant expression as first argument. I found, on StackOverflow, several example where throw was used instead of static_assert:

static_assert(<constante evaluated condition>,"message");
if (<condition>) {throw "message";}

My question is, is it legal to do that inside constexpr or consteval function.

For instance, if I want to check at compile time if some integer is positive (pretty dumb...) I may write:

CONSTSPEC void checkpos(int x) {
    if (x < 0) {
        // is this ill-formed, no diagnostic required (when called with a
        // negative argument?)
        throw "argument must be positive";
    }
}

LIVE

Where CONSTSPEC can be constexpr or consteval.

With constexpr

By looking at https://timsong-cpp.github.io/cppwp/n4861/dcl.constexpr#6 and also this answer it seems that calling checkpos with a strictly negative argument is ill-formed but no diagnostic is required, letting a compiler do whatever he wants, which would be useless for compile-time error detection.

With consteval

I can't find a word in the standard. Should I understand that then any call is well-formed and passing a strictly negative argument will result in a mandatory compile-time error?

Question

Is throw usable inside a constexpr function (I don't think so) or inside a consteval function (maybe) to generate a compile-time error?

like image 952
Oersted Avatar asked Sep 02 '25 04:09

Oersted


1 Answers

My question is, is it legal to [throw exceptions] inside constexpr or consteval function.

Before C++26, this would make the surrounding constant expression not a constant expression, which would usually have the same effect as a failed static_assert. If you don't intend to catch the exception anyway, you can just throw, goto, or do something else that's not allowed in constant expressions. People just use throw "message" by convention, but x: goto x; // message would have the same effect.

After P3068R6: Allowing exception throwing in constant-evaluation in C++26, you can also catch exceptions thrown at compile time. Considering that, it may be best to write:

throw std::invalid_argument("argument must be positive");

... which would work exactly as expected after C++26 and have a useful exception type, and behave like an "uncatchable exception" before that.

By looking at https://timsong-cpp.github.io/cppwp/n4861/dcl.constexpr#6 and also this answer it seems that calling checkpos with a strictly negative argument is ill-formed but no diagnostic is required, letting a compiler do whatever he wants, which would be useless for compile-time error detection.

No, you misunderstand that. The program would be ill-formed if you wrote a function like:

constexpr void f() { throw; }

The condition is that

no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression

... but in the case of checkpos, you could call checkpos(0), and that would be a constant expression. Whether you always call it with negative arguments or not doesn't matter. You could call it with some arguments that would make it a constant expression. For consteval, the same rules would apply since both constexpr and consteval make a function a "constexpr function".

Note that these restrictions no longer exist in C++23 after P2448R2: Relaxing some constexpr restrictions The declaration of f() above would be okay.

like image 198
Jan Schultke Avatar answered Sep 04 '25 18:09

Jan Schultke