C++26: Erroneous Behaviour

C++26: Erroneous Behaviour

By Sandor Dargo

Overload, 33(186):10-11, April 2025


C++’s undefined behaviour impacts safety. Sandor Dargo explains how and why uninitialised reads will become erroneous behaviour in C++26, rather than being undefined behaviour.

If you pick a random talk at a C++ conference these days, there is a fair chance that the speaker will mention safety at least a couple of times. It’s probably fine like that. The committee and the community must think about improving both the safety situation and the reputation of C++.

If you follow what’s going on in this space, you are probably aware that people have different perspectives on safety. I think almost everybody finds it important, but they would solve the problem in their own way.

A big source of issues is certain manifestations of undefined behaviour. It affects both the safety and the stability of software. I remember that a few years ago when I was working on some services which had to support a 10× growth, one of the important points was to eliminate undefined behaviour as much as possible. One main point for us was to remove uninitialized variables which often lead to crashing services.

Thanks to P2795R5 by Thomas Köppe, uninitialized reads won’t be undefined behaviour anymore – starting from C++26. Instead, they will get a new behaviour called ‘erroneous behaviour’.

The great advantage of erroneous behaviour is that it will work just by recompiling existing code. It will diagnose where you forgot to initialize variables. You don’t have to systematically go through your code and let’s say declare everything as auto to make sure that every variable has an initialized value. Which you probably wouldn’t do anyway.

But what is this new behaviour that on C++ Reference is even listed on the page of undefined behaviour? [CppRef-1] It’s well-defined, yet incorrect behaviour that compilers are recommended to diagnose. Is recommended enough?! Well, with the growing focus on safety, you can rest assured that an implementation that wouldn’t diagnose erroneous behaviour would be soon out of the game.

Some compilers can already identify uninitialized reads – what nowadays falls under undefined behaviour. For example, clang and gcc with -ftrivial-auto-var-init=zero have already offered default initialization of variables with automatic storage duration. This means that the technique to identify these variables is already there. The only thing that makes this approach not practical is that you will not know which variables you failed to initialize.

Instead of default initialization, with erroneous behaviour, an uninitialized object will be initialized to an implementation-specific value. Reading such a value is a conceptual error that is recommended and encouraged to be diagnosed by the compiler. That might happen through warnings, run-time errors, etc.

  void foo() {
    int d;  // d has an erroneous value
    bar(d); // that’s erroneous behaviour!
  }

So, looking at the above example, ideally int d; should be already diagnosed at compile-time as a warning. If it’s ignored, at some point, bar(d); will have an effect during program execution, but it should be well-defined, unlike undefined behaviour where anything can happen.

It’s worth noting that undefined behaviour and having erroneous values is not possible in constant expressions. In other words, constexpr protects from it.

Initializing an object to anything has a cost. What if you really want to avoid it and initialize the object later? Will you be able to still do it without getting the diagnostics? Sure! You just have to be deliberate about that. You cannot just leave values uninitialized by accident, you must mark them with C++26’s new attribute, [[indeterminate]].

We must notice in the example, that d doesn’t have an erroneous value anymore. Now its value is simply indeterminate [CppRef-2]. On the other hand, if we later use that variable still without initialization, it’s undefined behaviour!

Above, we’ve only talked about variables with automatic storage duration. That’s not the only way to have uninitialized variables. Moreover, probably it’s not even the main way, think about dynamic storage duration, think about pointers! Also, if any member is left uninitialized, the parent object’s value will be considered either indeterminate or erroneous. See Listing 1.

struct S {
  S() {}
  int num;
  std::string text;
};

int main() {
  [[indeterminate]] S s1; // indeterminate value
  std::cout << s1.num << '\n' 
        // this is UB as s1.num is indeterminate

  S s2;
  std::cout << s2.num << '\n' 
        // this is still UB, s2.num is an 
        // erroneous value
}
Listing 1

Not only variables but function parameters can also be marked [[indeterminate]]. See Listing 2.

struct S {
  S() {}
  int num;
  std::string text;
};

void foo(S s1 [[indeterminate]], S s2)
{
  bar(s1.num); // undefined behavior
  bar(s2.num); // erroneous behavior
}
Listing 2

At the point of writing (January 2025), no compiler provides support for erroneous behaviour.

Conclusion

C++26 introduces erroneous behaviour in order to give well-defined, but incorrect behaviour for reading uninitialized values. Soon, compilers will be recommended to diagnose every occurrence of reads of uninitialized variables and function parameters.

Also, if something is not initialized at a given moment on purpose, you can mark it with the [[indeterminate]] attribute following the don’t pay for what you don’t need principle.

This new behaviour is a nice step forward in terms of C++’s safety.

References

[CppRef-1] ‘Undefined behavior’ on cppreference.com, available at https://en.cppreference.com/w/cpp/language/ub

[CppRef-2] ‘C++ attribute: indeterminate’ on cppreference.com, available at https://en.cppreference.com/w/cpp/language/attributes/indeterminate

This article was previously published on Sandor Dargo’s Blog on 4 February 2025, and is available at https://www.sandordargo.com/blog/2025/02/05/cpp26-erroneous-behaviour

Sandor Dargo is is a passionate software craftsman focusing on reducing maintenance costs by applying and enforcing clean code standards. He loves knowledge sharing, both oral and written. When not reading or writing, he spends most of his time with his two children and wife in the kitchen or travelling.






Your Privacy

By clicking "Accept Non-Essential Cookies" you agree ACCU can store non-essential cookies on your device and disclose information in accordance with our Privacy Policy and Cookie Policy.

Current Setting: Non-Essential Cookies REJECTED


By clicking "Include Third Party Content" you agree ACCU can forward your IP address to third-party sites (such as YouTube) to enhance the information presented on this site, and that third-party sites may store cookies on your device.

Current Setting: Third Party Content EXCLUDED



Settings can be changed at any time from the Cookie Policy page.