Case 111: enumerable_thread_specific Lambda-Init Ambiguity¶
| Field | Value |
|---|---|
| Verdict | ๐ข COMPATIBLE |
| Category | Addition (Compatible) |
| Platforms | Linux, macOS, Windows |
| Flags | Bad practice |
Detected ChangeKinds |
func_added |
| Source files | browse on GitHub |
Category: Subtle source break / oneTBB regression suite | Verdict: ๐ข COMPATIBLE (known gap โ see below)
What breaks¶
A second constructor overload is added โ enumerable_thread_specific(int_factory_t)
(a function-pointer-typed factory). By itself this is a pure addition:
existing call sites that pass an int still resolve to the original
constructor. But consumer code patterns that previously had a single
viable conversion path can now become ambiguous, particularly with
brace-initialization or generic callable arguments. The risk is silent โ
code that compiled before may compile to a different constructor against
the new headers, or stop compiling at unrelated call sites that infer
the wrong overload.
Why function-pointer instead of std::function? A real oneTBB-flavoured
example would accept std::function<int()>, but pulling in
<functional> from libstdc++ 13 trips castxml/clang (__assume__
attribute in <bits/stl_bvector.h>), which would prevent the integration
test from ever running. A function-pointer typedef exhibits the same
overload-ambiguity risk for the purposes of this regression fixture.
Why this is in the oneTBB regression suite¶
Mirrors a documented oneTBB pain point: adding lambda-/functor-accepting constructor overloads to existing handle types introduced overload ambiguity in real downstream code. The pattern is repeatable across container-like types.
How abicheck catches it (and where it doesn't)¶
The diff exposes:
FUNC_ADDED: the newstd::function<int()>constructor
FUNC_ADDED on a constructor is COMPATIBLE โ by itself it cannot link-
or ABI-break anything. The follow-on overload ambiguity that breaks
downstream source compilation cannot be detected from snapshots alone:
it depends on the consumer's call-site context. This is a documented
known_gap.
Future work (see roadmap: case105 concept tightening) is the natural home for "constructor overload set risk-classification" because both need the castxml header-AST capture path to reason about call-site resolvability.
Code diff¶
| v1 | v2 |
|---|---|
enumerable_thread_specific(int); |
same โ plus a new overload |
| (no other ctors) | enumerable_thread_specific(int_factory_t); (typedef int (*int_factory_t)()) |
How to fix (as a library maintainer)¶
- Constrain the lambda-init overload with a SFINAE / concept that
excludes
int-convertible types. e.g.: This eliminates the ambiguity at the call site. - Or expose the lambda-init as a named factory
(
from_lambda(...)) rather than as an overloaded constructor.
References¶
- oneTBB issue tracker โ overload ambiguity in
enumerable_thread_specificconstructor set.
Source files¶
See also: Examples overview ยท All COMPATIBLE cases ยท Category: Addition (Compatible).