fix: operator[]/operator/ SFINAE in trailing return type for MSVC C++20 (issue #515)#686
Merged
kris-jusiak merged 1 commit intoMay 27, 2026
Conversation
9cfd37b to
1b4c7eb
Compare
…oost-ext#515) MSVC C++20 fails to deduce T in front::event::operator[] and front::state_impl::operator[] / operator/ when the SFINAE constraint is expressed as a default template parameter: template <class T, typename enable_if<callable<bool,T>::value, int>::type = 0> constexpr auto operator[](const T &t) const { ... } This pattern is known to cause template argument deduction failure in MSVC C++20 when the argument type is a dependent nested type such as front::not_<zero_wrapper<Guard>> (produced by !guard). Fix: move the enable_if constraint to the trailing return type instead. This form of SFINAE is handled correctly by all supported compilers including MSVC C++17 and C++20: template <class T> constexpr auto operator[](const T &t) const -> enable_if_t<callable<bool,T>::value, transition_eg<...>> { ... } Applied to all four overloads: - front::event::operator[] (event<e>[guard]) - front::event::operator/ (event<e>/action) - front::state_impl::operator[] (*state[guard]) - front::state_impl::operator/ (*state/action) Test: guards.cpp::negated_guard_compiles_on_event_and_state Exercises event<e>[!guard] and *state[!guard] patterns that previously failed to compile on MSVC C++20. Tested with: - GCC 14 / g++: C++17 and C++20 — all tests pass - macOS Clang: all tests pass (CI) - MSVC 19.51.36244 (VS 2022 17.x) via msvc-wine Docker: /std:c++17 — 30/33 pass (3 pre-existing C4458 shadow failures in sm_impl::process_event, unrelated to this fix) /std:c++20 — 33/33 pass (all tests including negated_guard_*)
1b4c7eb to
d5a0f6a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On MSVC C++20, writing
event<e>[!guard]or*state[!guard]fails to compile with a template argument deduction error. The same pattern works on GCC, Clang, and MSVC C++17.Root cause:
front::event::operator[]andfront::state_impl::operator[](and the correspondingoperator/overloads) express their SFINAE constraint as a default template parameter:MSVC C++20 has a known issue where this form of SFINAE can fail to deduce
Twhen the argument is a nested dependent type (here:front::not_<zero_wrapper<Guard>>produced by!guard). Specifically, MSVC's C++20 subscript-operator lookup interacts poorly with default-parameter SFINAE, causing it to reject the overload before deduction completes.Fix
Move the
enable_ifconstraint to the trailing return type, which is the standard and MSVC-compatible form of expression-SFINAE:This form of SFINAE has identical semantics — substitution failure removes the overload from the candidate set — but is evaluated strictly after deduction completes, which works correctly on all compilers.
Affected overloads
front::event::operator[]—event<e>[guard]/event<e>[!guard]front::event::operator/—event<e>/action(same MSVC risk, fixed for consistency)front::state_impl::operator[]—*state[guard]/*state[!guard]front::state_impl::operator/—*state/action(same MSVC risk, fixed for consistency)Test
Added
negated_guard_compiles_on_event_and_stateintest/ft/guards.cppthat exercises:*state1 + event<event1>[!guard](the primary reported failure)state2[!guard](the state-subscript form)Both patterns were already rejected by MSVC C++20 before this fix.
Compatibility
All existing GCC 14 and Clang tests pass unchanged (C++17 and C++20). The trailing-return-type form is valid C++14 and up.