Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

That is a complete misunderstanding.

A constexpr function can very well take an input, and it can invoke UB based on the runtime value of that input.

What the standard prohibits is compile-time evaluation of an expression which invokes UB. So, if you actually call your function at compile-time with a constexpr value that ends up invoking UB in the function (say, an integer overflow), THEN the standard mandates that the compiler throw an error rather than compiling some random value in.

For example:

  constexpr void foo(int x) {
    std::cout << 1024 * 1024 * 1024 * x;
  }

  int main() {
    static_assert(foo(100)); // will fail because computing 1024 * 1024 * 100 is signed integer overflow, which is UB
    foo(100); // invokes UB at runtime; in practice, will perhaps print some overflowed value
  }
Edit: I should also add that you can very well invoke UB in a constexpr expression if it is standard library UB and not core language UB (e.g. if you try to pop() from an empty std::vector).


"A constexpr function can very well take an input, and it can invoke UB based on the runtime value of that input. "

No, it cannot. It is not constant if it does that. As such, it cannot be used in in any context that requires a constant expression. It explicitly says the operations in a constexpr may not produce undefined behavior.

That isn't "may not produce undefined behavior except if you pass the wrong values at runtime or don't evaluate it". It says: "An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.8.1), would evaluate one of the following expressions:

an operation that would have undefined behavior "

In your case, the evaluation would evaluate (at runtime) an operation that would have undefined behavior.

It is true that where you do not require a constant expression, it does not require it be constexpr at all, but the question is whether an expression that produces undefined behavior is constexpr is "no".

The standard even clarified this to say that foo is simply considered non-constant in your example (IE it's not constexpr ).

IE see defect report 695

"The consensus of the CWG was that an expression like 1/0 should simply be considered non-constant; any diagnostic would result from the use of the expression in a context requiring a constant expression. "

In your example, foo is non-constant as used. It is not constexpr.


I suspect you are using "constexpr code/function" in a strange way. I believe most people use it to refer to code that is constexpr-qualified, such as "constexpr void foo() {...}". You seem to be using to refer to code that is a core constant expression, and that is where (most of) the misunderstanding comes from.

Still, it should be noted that even code that looks identical to a core constant expression can display certain kinds of UB if invoked at runtime, per the standard. For example, if the code is using certain STL classes or threading constructs in a way that is UB, it can still be considered a core constant expression. Additionally, the code may be well defined when used in a single-threaded context, but become UB when used in multi-threaded contexts, say if two threads are accessing an object member concurrently without the right barriers.

Here is an example on godbolt. Notice that bar() and baz() are defined the same way in the static_assert block and the runtime block, yet executing them constitutes UB in one case but not the other.

https://godbolt.org/z/3q5sTGe4j


When I say "a constexpr function", I mean "a function that is legally constexpr-qualified". An expression calling such a function with a constant argument is a core constant expression, as long as the evaluation of the function given that argument doesn't produce UB or leak memory.

But, this tells us nothing about whether calling the same function with some other argument would produce UB or not. Unless we try to form a core constant expression containing a call to that function for every possible argument value, we can't be sure if the function is free of UB for every possible input value.

My point, more simply, is: the fact that foo(1) is verified by the compiler to be a core constant expression is not a guarantee that foo(2) would be a core constant expression as well. Your original comment seems to me to imply that it does.

The consequence of this is that, as said elsewhere, this trick only helps to ensure that a unit test is not accidentally passing because of UB (and that memory is not leaked for that input value). It doesn't help provide any other guarantees.


You're both right, but you're talking about different things.

If you use the same values at run time as you use in the tests, the tests predict the behavior as desired.

If you use different values at run time as you use in the tests, the tests do not predict the behavior as desired.


> You're both right, but you're talking about different things.

The GP post is explicitly claiming that the values don't matter, and that per the standard a constexpr function should be guaranteed by the compiler to be incapable of producing UB (or else compilation should fail and it should not be allowed to be declared constexpr). This is simply false. Here is the GP statement:

>> That is, conditional constepxr code that depends on values and could produce UB is not valid constexpr code, and a compiler is not supposed to compile it. [emphasis mine]

> If you use the same values at run time as you use in the tests, the tests predict the behavior as desired.

Even this is not fully guaranteed by the standard, as core constant expressions must only be guaranteed not to invoke core language UB. They may still be considered core constant expressions and evaluated at compile time even if they exhibit standard library UB (e.g. if they are not respecting some preconditions of an std::vector method).


"The GP post is explicitly claiming that the values don't matter, and that per the standard a constexpr function should be guaranteed by the compiler to be incapable of producing UB (or else compilation should fail and it should not be allowed to be declared constexpr).

They are so guaranteed - as i cited to you, your example of foo, as used in foo(100), is considered not a constant. There is no such thing as a constexpr expression that can produce UB.

If it can produce UB, it is not constexpr.

That does not mean it will not be validly usable in non-constant contexts! But it is not constexpr.


But foo(1) is a constant expression, and the compiler accepts it.

Not sure what you mean by a "constexpr expression" exactly. My point is that a function can legally be constexpr-qualified even if it can produce UB when called with certain arguments. I think it's very common to call such a function "a constexpr function". Of course, we agree that not every expression where a constexpr function is called is a constant expression (such as foo(100) in my example). However, expressions where that function is called with arguments where it does not produce UB are legal constant expressions (such as foo(1) in my example).

Perhaps I misunderstood your original comment and we are in fact in violent agreement. If so I apologize.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: