Type associated constants would remove callbacks from C objects
N3969 proposes constants associated with structure types without adding object storage. Generic containers motivate the design, but type compatibility creates the difficult rules.
A generic hash map can know its hash function at compile time and still store a function pointer in every instance. That duplication motivates a proposal by Abdulmalek Almkainzi, N3969, listed on September 27 in the WG14 document log. It proposes constants associated with a structure or union type without contributing to the size or alignment of its objects. The difficult part is deciding when two structures with different associated values count as compatible types.
The revision deliberately changes the terminology. Its predecessor called these constexpr members, while N3969 calls them type associated constants and says they are not members. That distinction describes a real semantic choice. Instance storage contains the changing state of a map, while properties fixed for its type can live outside that storage. Access uses a type name instead of requiring a particular object.
C was my first language, and its importance for foreign interfaces remains one of the reasons I pay attention to its evolution. A proposal that changes how types carry information deserves particular care at translation-unit boundaries. The object layout is only one part of the interface. Compatible declarations must also agree about what operations on an object mean.
A container motivates the feature
The paper starts with a macro-generated structure holding hash and equality function pointers alongside key storage, value storage and the current length and capacity. The initialization operation assigns the callbacks to the object. When every map of a chosen kind uses the same callbacks, the design repeats information that could instead be fixed when that kind of map is declared.
N3969 argues that runtime function pointers make inlining less likely and enlarge the structure. Those are motivations in the proposal, not benchmark results. The optimization benefit would still depend on an implementation and the surrounding program. The storage distinction is explicit in the proposed semantics, which exclude the associated declarations from object size and alignment.
The paper compares this with a C++ template that accepts the hash and equality functions as template parameters. It also shows C header-template instantiation with the STC library, where preprocessor definitions configure an included header. The proposal seeks a more direct way to express the type-level information in C source. It does not propose replacing all callbacks, since an object whose callback genuinely varies at runtime still has different requirements.
Access starts with a parenthesized type
The proposed syntax places a constexpr declaration in a structure or union declaration list. A parenthesized type name followed by a dot accesses the associated constant. This is proposal syntax, not a C23 program.
struct configuration {
constexpr int limit = 10;
int current;
};
/* Proposed access syntax from N3969. */
int limit = (struct configuration).limit;
The field current belongs to an instance. The constant limit belongs to the type and does not add bytes to each instance. Proposed wording gives the associated object static storage duration and no linkage. Constants with the same name in the same structure or union type denote the same object.
The access expression is an lvalue under the proposed wording. The paper also changes the constant-expression rules so this access counts as a named constant. Those changes matter because simply allowing constexpr inside the declaration would not explain how an expression names the resulting object. The proposal needs both declaration rules and an expression form that identifies the appropriate type namespace.
Associated constants cannot be anonymous or bit-fields. The declaration must contain the constexpr storage-class specifier without another storage-class specifier. These constraints separate the feature from an arbitrary collection of declarations inside a structure. The design introduces a particular category of type-associated object with specific access and compatibility rules.
Compatibility includes callback identity within a translation unit
A map configured with one hash function cannot safely become a map configured with another merely because its ordinary fields have matching types. Stored entries were organized according to the original function. N3969 therefore requires corresponding associated constants in compatible structures within one translation unit to have matching names, compatible scalar types and equal values.
The paper illustrates this with two macro-generated map declarations using different hash functions. Initializing the second map from the first would violate a constraint because the types are incompatible. The callback choice becomes part of the type-level distinction, even though it contributes no per-instance storage. That is the central connection between the performance motivation and the type-system work.
Restricting equality checks to scalar types also limits the design. Structures carrying aggregate associated constants cannot become compatible through this mechanism. The paper acknowledges that this excludes interface structures and leaves expansion to future work. A feature intended to carry type-level information still needs a sufficiently precise account of how that information compares.
Across translation units, the proposal does not make compatibility depend on equality of the associated values. It cites static functions as the reason. Separate translation units can refer to their own static function definitions, whose pointers do not compare equal. This makes the distinction between local and cross-unit compatibility a consequential part of the proposal, rather than an incidental wording detail.
Dependencies limit the current implementation evidence
The paper identifies constexpr function-pointer initialization and tagless structure compatibility as dependencies that would make the feature more useful. Its proof of concept in a slimcc fork implements only part of the design on top of C23 rules. It therefore lacks those capabilities. The source explicitly distinguishes that experiment from the full proposed interaction between function pointers and compatible anonymous structures.
An alternative spelling using static constexpr and identifier::identifier would resemble C++. Almkainzi objects that requiring a single identifier on the left would exclude direct use of names such as struct S. The proposed parenthesized type syntax accommodates C code that does not introduce typedef names for every structure.
I find the compatibility question more consequential than the resemblance to C++ syntax. The proposal removes repeated callback storage only by making the callback part of how a type is described. Its same-unit and cross-unit rules then determine where the compiler can enforce that distinction. Those rules are the part an implementation experiment must exercise alongside the simpler demonstration that a type-associated integer occupies no instance storage.