C++OnSea 2025 SESSION ANNOUNCEMENT: Balancing Efficiency and Flexibility: Cost of Abstractions in Embedded Systems by Marcell Juhasz
Register now at https://cpponsea.uk/tickets/
C++OnSea 2025 SESSION ANNOUNCEMENT: Balancing Efficiency and Flexibility: Cost of Abstractions in Embedded Systems by Marcell Juhasz
Register now at https://cpponsea.uk/tickets/
C++OnSea 2025 SESSION ANNOUNCEMENT: Balancing Efficiency and Flexibility: Cost of Abstractions in Embedded Systems by Marcell Juhasz
Register now at https://cpponsea.uk/tickets/
Last week, we discussed language features that are becoming constexpr in C++26. Today, let’s turn our attention to the standard library features that will soon be usable at compile time. One topic is missing: exceptions. As they need both core language and library changes, I thought they deserved their own post. P2562R1: constexpr stable sorting This paper proposes making std::stable_sort, std::stable_partition, std::inplace_merge, and their ranges counterparts usable in constant expressions. While many algorithms have become constexpr over the years, this family related to stable sorting had remained exceptions — until now. The recent introduction of constexpr containers gives extra motivation for this proposal. If you can construct a container at compile time, it’s only natural to want to sort it there, too. More importantly, a constexpr std::vector can now support efficient, stable sorting algorithms. A key question is whether the algorithm can meet its computational complexity requirements under the constraints of constant evaluation. Fortunately, std::is_constant_evaluated() provides an escape hatch for implementations. For deeper details, check out the proposal itself. P1383R2: More constexpr for <cmath> and <complex> While P0533 made many <cmath> and <cstdlib> functions constexpr-friendly in C++23, it only addressed functions with trivial behavior — those no more complex than the basic arithmetic operators. Floating-point computations can yield different results depending on compiler settings, optimization levels, and hardware platforms. For instance, calculating std::sin(1e100) may produce varying outcomes due to the intricacies of floating-point arithmetic at such scales. The paper discusses these challenges and suggests that some variability in results is acceptable, given the nature of floating-point computations. The proposal accepts the need for a balance between strict determinism and practical flexibility. It suggests that while some functions should produce consistent results across platforms, others may inherently allow for some variability. P3074R7: trivial unions (was std::uninitialized<T>) To implement static, in-place, constexpr-friendly containers like non-allocating vectors, you often need uninitialized storage — typically via unions. However, default behavior for special members of unions has been limiting: if not all alternatives are trivial, the special member is deleted. This presents a problem for constexpr code where a no-op destructor isn’t quite the same as a trivial one. The road to solving this wasn’t short: P3074R7 went through seven revisions and considered five possible solutions—including library-based approaches, new annotations, and even a new union type. Ultimately, the committee decided to just make it work with minimal changes to the user experience. But how? For unions, the default constructor - if there is no default member initializer - is always going to be trivial. If the first alternative is an implicit-lifetime time, it begins its life-time and becomes the active member. The defaulted destructor is deleted if either the union has a user-provided default constructor or there exists a variant alternative that has a default member initializer and that member’s destructor is either deleted or inaccessible. Otherwise, the destructor is trivial. This excerpt from the proposal shows the changes well. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // trivial default constructor (does not start lifetime of s) // trivial destructor // (status quo: deleted default constructor and destructor) union U1 { string s; }; // non-trivial default constructor // deleted destructor // (status quo: deleted destructor) union U2 { string s = "hello"; } // trivial default constructor // starts lifetime of s // trivial destructor // (status quo: deleted default constructor and destructor) union U3 { string s[10]; } // non-trivial default constructor (initializes next) // trivial destructor // (status quo: deleted destructor) union U4 { string s; U4* next = nullptr; }; P3372R2: constexpr containers and adaptors Hana Dusíková authored a massive proposal that boils down to a simple goal: make (almost) all containers and adaptors constexpr. Up until now, only a handful of them were constexpr-friendly (std::vector, std::span, std::mdspan, std::basic_string and std::basic_string_view). From now on, the situation will be flipped. Almost everything will be constexpr-friendly. There is one exception and one constraint: std::hive is not included, because it doesn’t have a stable wording yet if you want to use unordered containers at compile-time, you must provide your own hashing facility, because std::hash cannot be made constexpr-friendly due to its requirements. Its result is guaranteed to be consistent only with the duration of the program. Happy days! P3508R0: Wording for “constexpr for specialized memory algorithms” Such a strange title, isn’t? Wording for something… As it turns out, there was already a paper accepted (P2283R2) making specialized memory algorithms constexpr-friendly. Algorithms that are essential for implementing constexpr container support, yet they were forgotten from C++20. These algorithms are (both in std and in std::ranges namespaces): uninitialized_value_construct uninitialized_value_construct_n uninitialized_copy uninitialized_copy_result uninitialized_copy_n uninitialized_copy_n_result uninitialized_move uninitialized_move_result uninitialized_move_n uninitialized_move_n_result uninitialized_fill uninitialized_fill_n When the paper was made, the necessary implementation change was to use std::construct_at instead of placement new, as std::consturct_at was already constexpr. But in the meantime, P2747R2 was accepted and placement new in the core language also became constexpr. Therefore, the implementation of the above functions doesn’t have to be changed, only their signatures have to be updated to support constexpr. Hence, the wording change. P3369R0: constexpr for uninitialized_default_construct We saw that the constexpr placement new affected P2283R2 and raised the need for a wording change performed in P3508R0. But that’s not the only side-effect it had. From the above-listed algorithm families, one is missing: uninitialized_default_construct. The reason is that uninitialized_default_construct cannot be implemented with std::construct_at as it always performs value initialization, default initialization was impossible. But with constexpr placement new this is not an issue anymore, therefore uninitialized_default_construct can also be turned into constexpr. Conclusion C++26 marks a huge step forward for constexpr support in the standard library. From stable sorting algorithms to containers, from tricky union rules to specialised memory functions, compile-time programming is becoming more and more supported. In the next article, we’ll cover compile-time exceptions! Connect deeper If you liked this article, please hit on the like button, subscribe to my newsletter and let’s connect on Twitter!
Last week, we discussed language features that are becoming constexpr in C++26. Today, let’s turn our attention to the standard library features that will soon be usable at compile time. One topic is missing: exceptions. As they need both core language and library changes, I thought they deserved their own post. P2562R1: constexpr stable sorting This paper proposes making std::stable_sort, std::stable_partition, std::inplace_merge, and their ranges counterparts usable in constant expressions. While many algorithms have become constexpr over the years, this family related to stable sorting had remained exceptions — until now. The recent introduction of constexpr containers gives extra motivation for this proposal. If you can construct a container at compile time, it’s only natural to want to sort it there, too. More importantly, a constexpr std::vector can now support efficient, stable sorting algorithms. A key question is whether the algorithm can meet its computational complexity requirements under the constraints of constant evaluation. Fortunately, std::is_constant_evaluated() provides an escape hatch for implementations. For deeper details, check out the proposal itself. P1383R2: More constexpr for <cmath> and <complex> While P0533 made many <cmath> and <cstdlib> functions constexpr-friendly in C++23, it only addressed functions with trivial behavior — those no more complex than the basic arithmetic operators. Floating-point computations can yield different results depending on compiler settings, optimization levels, and hardware platforms. For instance, calculating std::sin(1e100) may produce varying outcomes due to the intricacies of floating-point arithmetic at such scales. The paper discusses these challenges and suggests that some variability in results is acceptable, given the nature of floating-point computations. The proposal accepts the need for a balance between strict determinism and practical flexibility. It suggests that while some functions should produce consistent results across platforms, others may inherently allow for some variability. P3074R7: trivial unions (was std::uninitialized<T>) To implement static, in-place, constexpr-friendly containers like non-allocating vectors, you often need uninitialized storage — typically via unions. However, default behavior for special members of unions has been limiting: if not all alternatives are trivial, the special member is deleted. This presents a problem for constexpr code where a no-op destructor isn’t quite the same as a trivial one. The road to solving this wasn’t short: P3074R7 went through seven revisions and considered five possible solutions—including library-based approaches, new annotations, and even a new union type. Ultimately, the committee decided to just make it work with minimal changes to the user experience. But how? For unions, the default constructor - if there is no default member initializer - is always going to be trivial. If the first alternative is an implicit-lifetime time, it begins its life-time and becomes the active member. The defaulted destructor is deleted if either the union has a user-provided default constructor or there exists a variant alternative that has a default member initializer and that member’s destructor is either deleted or inaccessible. Otherwise, the destructor is trivial. This excerpt from the proposal shows the changes well. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // trivial default constructor (does not start lifetime of s) // trivial destructor // (status quo: deleted default constructor and destructor) union U1 { string s; }; // non-trivial default constructor // deleted destructor // (status quo: deleted destructor) union U2 { string s = "hello"; } // trivial default constructor // starts lifetime of s // trivial destructor // (status quo: deleted default constructor and destructor) union U3 { string s[10]; } // non-trivial default constructor (initializes next) // trivial destructor // (status quo: deleted destructor) union U4 { string s; U4* next = nullptr; }; P3372R2: constexpr containers and adaptors Hana Dusíková authored a massive proposal that boils down to a simple goal: make (almost) all containers and adaptors constexpr. Up until now, only a handful of them were constexpr-friendly (std::vector, std::span, std::mdspan, std::basic_string and std::basic_string_view). From now on, the situation will be flipped. Almost everything will be constexpr-friendly. There is one exception and one constraint: std::hive is not included, because it doesn’t have a stable wording yet if you want to use unordered containers at compile-time, you must provide your own hashing facility, because std::hash cannot be made constexpr-friendly due to its requirements. Its result is guaranteed to be consistent only with the duration of the program. Happy days! P3508R0: Wording for “constexpr for specialized memory algorithms” Such a strange title, isn’t? Wording for something… As it turns out, there was already a paper accepted (P2283R2) making specialized memory algorithms constexpr-friendly. Algorithms that are essential for implementing constexpr container support, yet they were forgotten from C++20. These algorithms are (both in std and in std::ranges namespaces): uninitialized_value_construct uninitialized_value_construct_n uninitialized_copy uninitialized_copy_result uninitialized_copy_n uninitialized_copy_n_result uninitialized_move uninitialized_move_result uninitialized_move_n uninitialized_move_n_result uninitialized_fill uninitialized_fill_n When the paper was made, the necessary implementation change was to use std::construct_at instead of placement new, as std::consturct_at was already constexpr. But in the meantime, P2747R2 was accepted and placement new in the core language also became constexpr. Therefore, the implementation of the above functions doesn’t have to be changed, only their signatures have to be updated to support constexpr. Hence, the wording change. P3369R0: constexpr for uninitialized_default_construct We saw that the constexpr placement new affected P2283R2 and raised the need for a wording change performed in P3508R0. But that’s not the only side-effect it had. From the above-listed algorithm families, one is missing: uninitialized_default_construct. The reason is that uninitialized_default_construct cannot be implemented with std::construct_at as it always performs value initialization, default initialization was impossible. But with constexpr placement new this is not an issue anymore, therefore uninitialized_default_construct can also be turned into constexpr. Conclusion C++26 marks a huge step forward for constexpr support in the standard library. From stable sorting algorithms to containers, from tricky union rules to specialised memory functions, compile-time programming is becoming more and more supported. In the next article, we’ll cover compile-time exceptions! Connect deeper If you liked this article, please hit on the like button, subscribe to my newsletter and let’s connect on Twitter!
C++26: more constexpr in the standard library
https://www.sandordargo.com/blog/2025/04/30/cpp26-constexpr-library-changes
#HackerNews #C++ #C++26 #constexpr #standardlibrary #programming
Last week, we discussed language features that are becoming constexpr in C++26. Today, let’s turn our attention to the standard library features that will soon be usable at compile time. One topic is missing: exceptions. As they need both core language and library changes, I thought they deserved their own post. P2562R1: constexpr stable sorting This paper proposes making std::stable_sort, std::stable_partition, std::inplace_merge, and their ranges counterparts usable in constant expressions. While many algorithms have become constexpr over the years, this family related to stable sorting had remained exceptions — until now. The recent introduction of constexpr containers gives extra motivation for this proposal. If you can construct a container at compile time, it’s only natural to want to sort it there, too. More importantly, a constexpr std::vector can now support efficient, stable sorting algorithms. A key question is whether the algorithm can meet its computational complexity requirements under the constraints of constant evaluation. Fortunately, std::is_constant_evaluated() provides an escape hatch for implementations. For deeper details, check out the proposal itself. P1383R2: More constexpr for <cmath> and <complex> While P0533 made many <cmath> and <cstdlib> functions constexpr-friendly in C++23, it only addressed functions with trivial behavior — those no more complex than the basic arithmetic operators. Floating-point computations can yield different results depending on compiler settings, optimization levels, and hardware platforms. For instance, calculating std::sin(1e100) may produce varying outcomes due to the intricacies of floating-point arithmetic at such scales. The paper discusses these challenges and suggests that some variability in results is acceptable, given the nature of floating-point computations. The proposal accepts the need for a balance between strict determinism and practical flexibility. It suggests that while some functions should produce consistent results across platforms, others may inherently allow for some variability. P3074R7: trivial unions (was std::uninitialized<T>) To implement static, in-place, constexpr-friendly containers like non-allocating vectors, you often need uninitialized storage — typically via unions. However, default behavior for special members of unions has been limiting: if not all alternatives are trivial, the special member is deleted. This presents a problem for constexpr code where a no-op destructor isn’t quite the same as a trivial one. The road to solving this wasn’t short: P3074R7 went through seven revisions and considered five possible solutions—including library-based approaches, new annotations, and even a new union type. Ultimately, the committee decided to just make it work with minimal changes to the user experience. But how? For unions, the default constructor - if there is no default member initializer - is always going to be trivial. If the first alternative is an implicit-lifetime time, it begins its life-time and becomes the active member. The defaulted destructor is deleted if either the union has a user-provided default constructor or there exists a variant alternative that has a default member initializer and that member’s destructor is either deleted or inaccessible. Otherwise, the destructor is trivial. This excerpt from the proposal shows the changes well. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // trivial default constructor (does not start lifetime of s) // trivial destructor // (status quo: deleted default constructor and destructor) union U1 { string s; }; // non-trivial default constructor // deleted destructor // (status quo: deleted destructor) union U2 { string s = "hello"; } // trivial default constructor // starts lifetime of s // trivial destructor // (status quo: deleted default constructor and destructor) union U3 { string s[10]; } // non-trivial default constructor (initializes next) // trivial destructor // (status quo: deleted destructor) union U4 { string s; U4* next = nullptr; }; P3372R2: constexpr containers and adaptors Hana Dusíková authored a massive proposal that boils down to a simple goal: make (almost) all containers and adaptors constexpr. Up until now, only a handful of them were constexpr-friendly (std::vector, std::span, std::mdspan, std::basic_string and std::basic_string_view). From now on, the situation will be flipped. Almost everything will be constexpr-friendly. There is one exception and one constraint: std::hive is not included, because it doesn’t have a stable wording yet if you want to use unordered containers at compile-time, you must provide your own hashing facility, because std::hash cannot be made constexpr-friendly due to its requirements. Its result is guaranteed to be consistent only with the duration of the program. Happy days! P3508R0: Wording for “constexpr for specialized memory algorithms” Such a strange title, isn’t? Wording for something… As it turns out, there was already a paper accepted (P2283R2) making specialized memory algorithms constexpr-friendly. Algorithms that are essential for implementing constexpr container support, yet they were forgotten from C++20. These algorithms are (both in std and in std::ranges namespaces): uninitialized_value_construct uninitialized_value_construct_n uninitialized_copy uninitialized_copy_result uninitialized_copy_n uninitialized_copy_n_result uninitialized_move uninitialized_move_result uninitialized_move_n uninitialized_move_n_result uninitialized_fill uninitialized_fill_n When the paper was made, the necessary implementation change was to use std::construct_at instead of placement new, as std::consturct_at was already constexpr. But in the meantime, P2747R2 was accepted and placement new in the core language also became constexpr. Therefore, the implementation of the above functions doesn’t have to be changed, only their signatures have to be updated to support constexpr. Hence, the wording change. P3369R0: constexpr for uninitialized_default_construct We saw that the constexpr placement new affected P2283R2 and raised the need for a wording change performed in P3508R0. But that’s not the only side-effect it had. From the above-listed algorithm families, one is missing: uninitialized_default_construct. The reason is that uninitialized_default_construct cannot be implemented with std::construct_at as it always performs value initialization, default initialization was impossible. But with constexpr placement new this is not an issue anymore, therefore uninitialized_default_construct can also be turned into constexpr. Conclusion C++26 marks a huge step forward for constexpr support in the standard library. From stable sorting algorithms to containers, from tricky union rules to specialised memory functions, compile-time programming is becoming more and more supported. In the next article, we’ll cover compile-time exceptions! Connect deeper If you liked this article, please hit on the like button, subscribe to my newsletter and let’s connect on Twitter!
C++26: more constexpr in the core language | Hacker News
LinkC++26:核心語言中更多 constexpr 功能的演進Since constexpr was added to the language in C++11, its scope has been gradually expanded. In the beginning, we couldn’t even use if, else or loops, which were changed in C++14. C++17 added support for constexpr lambdas. C++20 added the ability to use allocation and use std::vector and std::string in constant expressions. In this article, let’s see how constexpr evolves with C++26. To be more punctual, let’s see what language features become more constexpr-friendly. We’ll discuss library changes in a separate article, as well as constexpr exceptions, which need both language and library changes. P2738R1: constexpr cast from void* Thanks to the acceptance of P2738R1, starting from C++26, one can cast from void* to a pointer of type T in constant expressions, if the type of the object at that adress is exactly the type of T. Note that conversions to interconvertible - including pointers to base classes - or not related types are not permitted. The motivation behind this change is to make several standard library functions or types work at compile time. To name a few examples: std::format, std::function, std::function_ref, std::any. The reason why this change will allow many more for more constexpr in the standard library is that storing void* is a commonly used compilation firewall technique to reduce template instantiations and the number of symbols in compiled binaries. P2747R2: constexpr placement new As std::construct_at is a limited tool that only allows to perform value initialization but not others such as default or list initialization, there has been a need to make placement new usable in constant expressions. At the same time, placement new is a very, maybe even too flexible tool and to use it in a safe way requires casting to void* and then back to T*. This faced some issues, but the acceptance of P2738R1 and the ability of casting from void* in constant expressions made the impossible possible. If you are looking for more details, check P2747R2. P2686R5: constexpr structured bindings and references to constexpr variables This is a rather long (20 pages) proposal and I found it not particularly easy to read. That’s not the fault of the authors, the problem is hard to address. The paper which is based on another, went through 5 revisions, discusses various solutions, and lists the wording changes on more than 10 pages. Long story short, you’ll be able to declare structured bindings constexpr. As structured bindings behave like references, the same restrictions apply as to constexpr references. Those restrictions become more relaxed. Before, a constexpr reference had to bind to a variable with static storage duration, so that the address doesn’t change from one evaluation to another. With C++26, in addition, variables with automatic storage duration are also accepted if and only if the address is constant relative to the stack frame in which the reference or the structured binding lives. In practice, this means that you cannot have a constexpr reference in a lambda to bind to an enclosing function. The reason is that in order to access that variable, the expression is something like this->__x where __x represents the captured address of x. As we don’t know at compile time what object this points to, it’s not a constant expression. Conclusion In this article, we reviewed how constexpr evolves in the C++26 core language. We are getting constexpr cast from void*, placement new, structured bindings and even exceptions (not discussed today). In the next article, we’ll see how the standard library’s constexpr support evolves. Connect deeper If you liked this article, please hit on the like button, subscribe to my newsletter and let’s connect on Twitter!
C++26: more constexpr in the core language
https://www.sandordargo.com/blog/2025/04/23/cpp26-constexpr-language-changes
#HackerNews #C++ #C++26 #constexpr #programming #language #changes #tech #news
Since constexpr was added to the language in C++11, its scope has been gradually expanded. In the beginning, we couldn’t even use if, else or loops, which were changed in C++14. C++17 added support for constexpr lambdas. C++20 added the ability to use allocation and use std::vector and std::string in constant expressions. In this article, let’s see how constexpr evolves with C++26. To be more punctual, let’s see what language features become more constexpr-friendly. We’ll discuss library changes in a separate article, as well as constexpr exceptions, which need both language and library changes. P2738R1: constexpr cast from void* Thanks to the acceptance of P2738R1, starting from C++26, one can cast from void* to a pointer of type T in constant expressions, if the type of the object at that adress is exactly the type of T. Note that conversions to interconvertible - including pointers to base classes - or not related types are not permitted. The motivation behind this change is to make several standard library functions or types work at compile time. To name a few examples: std::format, std::function, std::function_ref, std::any. The reason why this change will allow many more for more constexpr in the standard library is that storing void* is a commonly used compilation firewall technique to reduce template instantiations and the number of symbols in compiled binaries. P2747R2: constexpr placement new As std::construct_at is a limited tool that only allows to perform value initialization but not others such as default or list initialization, there has been a need to make placement new usable in constant expressions. At the same time, placement new is a very, maybe even too flexible tool and to use it in a safe way requires casting to void* and then back to T*. This faced some issues, but the acceptance of P2738R1 and the ability of casting from void* in constant expressions made the impossible possible. If you are looking for more details, check P2747R2. P2686R5: constexpr structured bindings and references to constexpr variables This is a rather long (20 pages) proposal and I found it not particularly easy to read. That’s not the fault of the authors, the problem is hard to address. The paper which is based on another, went through 5 revisions, discusses various solutions, and lists the wording changes on more than 10 pages. Long story short, you’ll be able to declare structured bindings constexpr. As structured bindings behave like references, the same restrictions apply as to constexpr references. Those restrictions become more relaxed. Before, a constexpr reference had to bind to a variable with static storage duration, so that the address doesn’t change from one evaluation to another. With C++26, in addition, variables with automatic storage duration are also accepted if and only if the address is constant relative to the stack frame in which the reference or the structured binding lives. In practice, this means that you cannot have a constexpr reference in a lambda to bind to an enclosing function. The reason is that in order to access that variable, the expression is something like this->__x where __x represents the captured address of x. As we don’t know at compile time what object this points to, it’s not a constant expression. Conclusion In this article, we reviewed how constexpr evolves in the C++26 core language. We are getting constexpr cast from void*, placement new, structured bindings and even exceptions (not discussed today). In the next article, we’ll see how the standard library’s constexpr support evolves. Connect deeper If you liked this article, please hit on the like button, subscribe to my newsletter and let’s connect on Twitter!
Okay, let's see how long it takes for this feature to get implemented in clang …
C++Now 2025 SESSION ANNOUNCEMENT: Harnessing constexpr - A Path to Safer C++ by Mikhail Svetkin
https://schedule.cppnow.org/session/2025/harnessing-constexpr/
Register now at https://cppnow.org/registration/