Case 223: Header-Only Template Default Changed¶
| Field | Value |
|---|---|
| Verdict | 🟠 API_BREAK |
| Category | API Break |
| Classification | Rule |
| Platforms | Linux, macOS, Windows |
| Flags | API break |
Detected ChangeKinds |
template_param_default_changed |
| Source files | catalog/cases/case223_header_only_template_default_changed/ |
| Rule family | header-only-template-default-changed |
| Subject | Template and generic-programming issues |
Category: Template ABI | Verdict: 🟢 NO_CHANGE (castxml) / 🟠 API_BREAK (clang)
Verdict and consumer impact¶
mylib::Box<T, U = int> is a header-only class template. Its default for U
changes from int to long. Nothing exports Box, and no public signature
names it, so no symbol changes. A consumer that writes mylib::Box<char> still
compiles against v2, but it now selects Box<char, long> instead of
Box<char, int>. The change is source-level, not binary.
On the default castxml lane the run reads NO_CHANGE, with run status partial
and the template-default assurance gap: castxml cannot record template parameter
defaults, so it cannot see this change. The clang backend records the defaults
and reports template_param_default_changed as API_BREAK.
Old/new diff¶
| v1.h | v2.h |
|---|---|
template <typename T, typename U = int> struct Box; |
template <typename T, typename U = long> struct Box; |
abicheck command¶
cat > .abicheck.yml <<'EOF'
compile:
frontend: castxml
lang: c++
EOF
abicheck dump -H v1.h --config .abicheck.yml --depth headers -o old.json
abicheck dump -H v2.h --config .abicheck.yml --depth headers -o new.json
abicheck compare old.json new.json
Switch frontend: clang to see the clang result.
Expected abicheck finding¶
castxml (default):
clang:
Verdict: API_BREAK
- template_param_default_changed: class template Box has a different
default for parameter 1 (int -> long)
Minimum evidence¶
min_evidence: L2, the public header.
Why abicheck catches it¶
The clang dumper records TemplateTypeParmDecl.defaultArg for class templates.
compare/template_param_defaults.py pairs the pattern by qualified name and
compares the recorded defaults. castxml writes no record for an uninstantiated
template, so its snapshot stays UNKNOWN and the run reports the gap.
Runtime failure demonstration¶
None. Nothing is exported, so no binary changes. The break is at the source level, and a consumer rebuilds.
Safe redesign¶
Do not change a default in a public header without a deprecation window. Add a
new template alias, for example template <typename T> using BoxLong = Box<T, long>;,
and leave Box's default unchanged.
Source files¶
CMakeLists.txtapp.cppconsumer_smoke.cppv1.cppv1.hv2.cppv2.h
See also: Compatibility Catalog · All API_BREAK cases · Category: API Break · Rule: Header-only template default changed · Subject: Template and generic-programming issues.