The sum-delegating monadic statics constrain only the grade, not the callback:
fn::optional's sum-case _transform (include/fn/optional.hpp:233) — requires some_sum<T> only; the callback is first checked at :236, inside the deduced-return body.
fn::expected's sum-case _transform (include/fn/expected.hpp:239) — same (its is_constructible_v<E, …> conjunct is present and works; asserted with a move-only E).
fn::expected's sum-case _transform_error (expected.hpp:302) — requires some_sum<E> only.
Because the body is instantiated during return-type deduction (candidate-signature formation), the callback check happens outside the immediate context:
- A non-invocable callback is a hard error, where the same call on
and_then (optional.hpp:147, expected.hpp:82) or on the non-sum/pack transform (optional.hpp:220, expected.hpp:219) cleanly SFINAE-drops. Verified on g++ and clang++ with dependent probes.
- Overload-resolution poisoning: on a non-const lvalue, the losing
const& candidate still instantiates its body, so a category-partial visitor (e.g. handlers taking int& only) makes the call ill-formed even though the & overload it would select is fine. This is why the sum tests historically had to use 4-category-exhaustive visitors.
Fix direction: mirror sum::transform's own constraint (typelist_invocable, sum.hpp:544) in these statics' requires-clauses, so validity is decided in the immediate context.
Tests: tests/fn/optional.cpp (commit f7ca86e) and tests/fn/expected.cpp (commit b88b64e), branch bronek/unit_tests_cleanup, document the gap with GAP comments and const&-handler visitors; negative requires probes can be added once the constraint exists.
The sum-delegating monadic statics constrain only the grade, not the callback:
fn::optional's sum-case_transform(include/fn/optional.hpp:233) —requires some_sum<T>only; the callback is first checked at :236, inside the deduced-return body.fn::expected's sum-case_transform(include/fn/expected.hpp:239) — same (itsis_constructible_v<E, …>conjunct is present and works; asserted with a move-onlyE).fn::expected's sum-case_transform_error(expected.hpp:302) —requires some_sum<E>only.Because the body is instantiated during return-type deduction (candidate-signature formation), the callback check happens outside the immediate context:
and_then(optional.hpp:147,expected.hpp:82) or on the non-sum/packtransform(optional.hpp:220,expected.hpp:219) cleanly SFINAE-drops. Verified on g++ and clang++ with dependent probes.const&candidate still instantiates its body, so a category-partial visitor (e.g. handlers takingint&only) makes the call ill-formed even though the&overload it would select is fine. This is why the sum tests historically had to use 4-category-exhaustive visitors.Fix direction: mirror
sum::transform's own constraint (typelist_invocable,sum.hpp:544) in these statics' requires-clauses, so validity is decided in the immediate context.Tests:
tests/fn/optional.cpp(commit f7ca86e) andtests/fn/expected.cpp(commit b88b64e), branchbronek/unit_tests_cleanup, document the gap with GAP comments and const&-handler visitors; negativerequiresprobes can be added once the constraint exists.