Skip to content

Case 87: Default template argument changed

Field Value
Verdict ๐Ÿ”ด BREAKING
Category Breaking
Platforms Linux, macOS, Windows
Flags ABI break
Detected ChangeKinds default_template_arg_changed
Source files examples/case87_default_template_arg_changed/

Category: Template ABI | Verdict: BREAKING

What breaks

// v1
template <typename Float, typename Distance = minkowski_distance<Float>>
class descriptor;

// v2
template <typename Float, typename Distance = euclidean_distance<Float>>
class descriptor;

Consumer source like mylib::descriptor<float> d; compiles against both headers โ€” the change is invisible at the call site. But the substituted type differs:

  • v1: descriptor<float, minkowski_distance<float>> โ†’ mangled symbol _ZN5mylib10descriptorIfNS_18minkowski_distanceIfEEEC1Ev
  • v2: descriptor<float, euclidean_distance<float>> โ†’ mangled symbol _ZN5mylib10descriptorIfNS_18euclidean_distanceIfEEEC1Ev

A consumer compiled against v1 emits calls to the first mangled name. A consumer compiled against v2 emits calls to the second. The shipped library can only contain one โ€” whichever version was built. Cross-version linking yields unresolved symbols.

Real Failure Demo

Severity: API BREAK / LOAD-TIME FAILURE

cmake -S examples -B /tmp/abicheck-examples-build -DCMAKE_BUILD_TYPE=Debug
cmake --build /tmp/abicheck-examples-build --target case86_default_template_arg_changed_app case86_default_template_arg_changed_v2

tmp=$(mktemp -d)
cp /tmp/abicheck-examples-build/case86_default_template_arg_changed/app_v1 "$tmp/"
cp /tmp/abicheck-examples-build/case86_default_template_arg_changed/libv2.so "$tmp/libv1.so"
(cd "$tmp" && LD_LIBRARY_PATH=. ./app_v1)
# ./app_v1: symbol lookup error: undefined symbol: descriptor<float, minkowski_distance<float>>::dimension()

Why distinct from case32

case32 covers function default parameter values, which are resolved at the call site and never affect mangling โ€” verdict NO_CHANGE. case87 covers template default arguments, which ARE part of the substituted type and DO affect mangling โ€” verdict BREAKING. Opposite outcome, opposite mechanism, same English phrase ("default value changed"). Easy to confuse without a dedicated case.

How abicheck catches it

The diff produces func_removed for the v1-mangled constructor symbol and func_added for the v2-mangled one. The new default_template_arg_changed detector correlates them when the unqualified function name is identical and the substituted template argument differs only in the inner-type chain. The grouped finding points to the header line where the default was changed so the report explains why the symbols differ.

Real-world reference

cpp/oneapi/dal/algo/knn/common.hpp:

template <typename Float = float,
          typename Method = method::by_default,
          typename Task = task::by_default,
          typename Distance = oneapi::dal::minkowski_distance::descriptor<Float>>
class descriptor;

Any change to the default for Method, Task, or Distance re-mangles every explicit instantiation that didn't override that argument.


Source files

  • CMakeLists.txt
  • app.cpp
  • v1.cpp
  • v1.h
  • v2.cpp
  • v2.h

See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.