A pack never holds a sum — pack.hpp:36 static_asserts it, and pack_impl.hpp:102-105 deletes
_append<T> for _some_sum<T>. But the rejection never reaches either guard, and the two
supported compilers disagree about what happens instead.
The ambiguity
_pack_append's partial specializations overlap for a sum argument (pack_impl.hpp:26-35):
template <typename T, typename... Ts>
requires _some_sum<T>
struct _pack_append<T, Ts...>; // (A) declared, never defined
template <typename T, typename... Ts>
requires(not _some_pack<T>)
struct _pack_append<T, Ts...> { ... }; // (B) defined — a sum is not a pack, so this matches too
For T = sum<int> both are satisfied and neither constraint subsumes the other, so the partial
specialization is ambiguous ([temp.spec.partial.match]/2 — ill-formed).
The divergence
append's trailing return type is -> append_type<T>, i.e. _pack_append<T, Ts...>::type. A
trailing return type is part of the signature and is substituted before the trailing
requires-clause is checked, so the ambiguity fires first — the deleted _append<sum> overload is
unreachable dead code. (The codebase already knows this hazard: see the comment at
fn/detail/functional.hpp:93-95 explaining why invoke uses a deduced return instead.)
template <typename P, typename T>
concept can_append_in_place = requires(P p, T t) { static_cast<P &&>(p).append(std::in_place_type<T>, t); };
static_assert(can_append_in_place<fn::pack<int> &, int>); // sanity
static_assert(not can_append_in_place<fn::pack<int> &, fn::sum<int>>);
| compiler |
result |
| g++ 16.1.1 |
hard error — ambiguous template instantiation for 'struct fn::detail::_pack_append<fn::sum<int>, int>', even inside the requires-expression |
| clang 22.1.6 |
compiles; the concept correctly yields false |
So on clang the rejection is SFINAE-clean and generic code can ask "can I append this?"; on gcc the
question itself fails to compile. Same source, two answers.
Fix (verified)
Make the two specializations mutually exclusive, and give the sum case a body with no type member
so ::type is a plain substitution failure ("no member named type") rather than a use of an
incomplete type:
template <typename T, typename... Ts>
requires _some_sum<T>
struct _pack_append<T, Ts...> {}; // defined, but no `type`
template <typename T, typename... Ts>
requires(not _some_pack<T>) && (not _some_sum<T>) // <-- excludes sums
struct _pack_append<T, Ts...> { ... };
With this, both compilers accept the probe above and agree the append is not viable. I verified
this locally against g++ 16.1.1 and clang 22.1.6 and then reverted it — the fix belongs on its own
branch, and a fix PR should also confirm the full suite and decide whether the now-reachable deleted
_append<sum> overload is still wanted.
Found while adding requires-clause coverage to tests/fn/pack.cpp (#85). tests/fn/pack.cpp
carries a GAP comment at the site rather than a negative probe, since the probe cannot be written
portably until this is fixed. Same class as #277 (validity decided outside the immediate context).
A
packnever holds asum—pack.hpp:36static_asserts it, andpack_impl.hpp:102-105deletes_append<T>for_some_sum<T>. But the rejection never reaches either guard, and the twosupported compilers disagree about what happens instead.
The ambiguity
_pack_append's partial specializations overlap for a sum argument (pack_impl.hpp:26-35):For
T = sum<int>both are satisfied and neither constraint subsumes the other, so the partialspecialization is ambiguous ([temp.spec.partial.match]/2 — ill-formed).
The divergence
append's trailing return type is-> append_type<T>, i.e._pack_append<T, Ts...>::type. Atrailing return type is part of the signature and is substituted before the trailing
requires-clause is checked, so the ambiguity fires first — the deleted
_append<sum>overload isunreachable dead code. (The codebase already knows this hazard: see the comment at
fn/detail/functional.hpp:93-95explaining whyinvokeuses a deduced return instead.)ambiguous template instantiation for 'struct fn::detail::_pack_append<fn::sum<int>, int>', even inside therequires-expressionfalseSo on clang the rejection is SFINAE-clean and generic code can ask "can I append this?"; on gcc the
question itself fails to compile. Same source, two answers.
Fix (verified)
Make the two specializations mutually exclusive, and give the sum case a body with no
typememberso
::typeis a plain substitution failure ("no member namedtype") rather than a use of anincomplete type:
With this, both compilers accept the probe above and agree the append is not viable. I verified
this locally against g++ 16.1.1 and clang 22.1.6 and then reverted it — the fix belongs on its own
branch, and a fix PR should also confirm the full suite and decide whether the now-reachable deleted
_append<sum>overload is still wanted.Found while adding
requires-clause coverage totests/fn/pack.cpp(#85).tests/fn/pack.cppcarries a GAP comment at the site rather than a negative probe, since the probe cannot be written
portably until this is fixed. Same class as #277 (validity decided outside the immediate context).