constexpr virtual inheritance
This paper proposes allowing usage of virtual inheritance. This will allow in future constexpr
-ification of std::ios_base
, and streams, removing final limitation for making most of stream formatting constexpr
.
This will also remove definition of constexpr-suitable as it will be meaningless and clean all references across draft of the standard (if this paper land after P3367: constexpr coroutines).
Changes
- R1 → R2: Removed wording based on P3367: constexpr coroutines as this paper targets C++26, and coroutines C++29.
- R0 → R1: Added alternative wording in case it will land before P3367: constexpr coroutines.
Motivation
Last language syntax thing which is disallowed in [dcl.constexpr] which blocks us from making std::stringstream
constant evaluatable, which blocks us from making exception types for <chrono>
(chrono::nonexistent_local_time
and chrono::ambiguous_local_time
). Stream formatting <chrono>
. Using basic_istream_view
in range code for compile-time parsing.
When we remove this limitations, language will only have limit on evaluation properties of code in [expr.const], and not syntactical (with exception of reinterpret_cast
, but this is also defined as not constant evaluatable.)
Then keyword constexpr
can be seen only as an opt-in into constant evaluatable and can be later removed and replaced with an opt-out attribute (this is not propesed here.)
Example of allowed code
struct Superbase {
string id{"name"};
};
struct Common: Superbase {
unsigned counter{0};
};
struct Left: virtual Common {
unsigned value{0};
constexpr const unsigned & get_counter() const {
return Common::counter;
}
};
struct Right: virtual Common {
unsigned value{0};
constexpr const unsigned & get_counter() const {
return Common::counter;
}
};
struct Child: Left, Right {
unsigned x{0};
unsigned y{0};
// ...
};
constexpr auto ch = Child{}; // before: not allowed to even construct
// after: works as expected
static_assert(&ch.Left::get_counter() == &ch.Right::get_counter());
Hierarchy
Clang's representation
Implementation experience
In progress in clang's fork. Currently structures are represented as APValue
object with type field struct
and pointer to an array of APValue
-s containing n
APValue
-s representing base types and k
APValue
representing each member subobject.
Lookup for outermost object is already implemented in clang in order to implement virtual
function calls. So only changes needed are:
- Allow functions to be constexpr if they are within type with virtual bases (in SemaDeclCXX.cpp, note: clang currently disallows not just constructor/destructor but all member functions to be constexpr)
- Allow default constructors for types with virtual bases (in SemaType.cpp)
- Allow computing dynamic type for objects (in ExprConstant.cpp)
- Extend
APValue
to contain number of virtual bases (in APValue.h) - Make construction of outermost object contain subobject representing virtual bases (in ExprConstant.cpp)
- Handle destruction of outermost object to properly destroy also virtual bases (in ExprConstant.cpp)
- Handle zero-initialization of outermost object (in ExprConstant.cpp)
I spoke with other implementors and they don't seem to have any major concern about implementability.
Library impact
Removing last limitation on constexpr-suitable as defined in [dcl.constexpr] will make it a tautology as every function will now be constexpr-suitable hence this paper contains changes to removal of it across wording.
But this term was used somehow badly to make specify what must be constant evaluatable. It's a subject of LWG issue 2833. Library needs to invent wording specifying something like "implementation must make sure this functionality is constant evaluatable by avoiding constructs disallowed in [expr.const]". As constexpr
keyword doesn't mean something must be constant-evaluatable, it's just an opt-in into evaluation for some (or none) code-paths.
I still kept removal of all references to constexpr-suitable in this paper for the project editor's convenience but I guess LWG will do some changes there as they will resolve the issue 2833.
Proposed changes to wording
Paper contains two sets of wordings. One is based on current draft and one is based on changes in P3367 constexpr coroutines.
Based on current draft
9.2.6 The constexpr and consteval specifiers [dcl.constexpr]
- it is not a coroutine ([dcl.fct.def.coroutine]), and.
- if the function is a constructor or destructor, its class does not have any virtual base classes.
Feature test macro
15.11 Predefined macro names [cpp.predefined]
__cpp_constexpr_virtual_inheritance 2025??L