https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116038
Bug ID: 116038 Summary: [14/15 Regression] ambiguous overload in bind_front caused by deducing this Product: gcc Version: 14.1.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: valentin at tolmer dot fr Target Milestone: --- The following snippet compiles well until 13.3 and breaks with 14.1 (and trunk), where it complains of ambiguous overload: $ cat <source> #include<functional> template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; struct A{}; struct B{}; int apply_a(int i, A) { return i; } int apply_b(int i, B) { return i; } int main() { overloaded{ std::bind_front(apply_a, 1), std::bind_front(apply_b, 2), }(A()); return 0; } $ g++ -std=c++2b <source> In file included from <source>:1: /opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional: In instantiation of 'static constexpr decltype(auto) std::_Bind_front<_Fd, _BoundArgs>::_S_call(_Tp&&, std::index_sequence<_Is ...>, _CallArgs&& ...) [with _Tp = overloaded<std::_Bind_front<int (*)(int, A), int>, std::_Bind_front<int (*)(int, B), int> >; long unsigned int ..._Ind = {0}; _CallArgs = {A}; _Fd = int (*)(int, A); _BoundArgs = {int}; std::index_sequence<_Is ...> = std::integer_sequence<long unsigned int, 0>]': /opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:947:18: required from 'constexpr std::invoke_result_t<decltype (forward_like<_Self>(declval<_Fd>())), decltype (forward_like<_Self>(declval<_BoundArgs>()))..., _CallArgs ...> std::_Bind_front<_Fd, _BoundArgs>::operator()(this _Self&&, _CallArgs&& ...) [with _Self = overloaded<std::_Bind_front<int (*)(int, A), int>, std::_Bind_front<int (*)(int, B), int> >; _CallArgs = {A}; _Fd = int (*)(int, A); _BoundArgs = {int}; std::invoke_result_t<decltype (forward_like<_Self>(declval<_Fd>())), decltype (forward_like<_Self>(declval<_BoundArgs>()))..., _CallArgs ...> = int; decltype (forward_like<_Self>(declval<_Fd>())) = int (*&&)(int, A)]' 947 | return _S_call(std::forward<_Self>(__self), _BoundIndices(), | ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 948 | std::forward<_CallArgs>(__call_args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <source>:12:8: required from here 9 | overloaded{ | ~~~~~~~~~~~ 10 | std::bind_front(apply_a, 1), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 11 | std::bind_front(apply_b, 2), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 12 | }(A()); | ~^~~~~ /opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:1018:53: error: request for member '_M_fd' is ambiguous 1018 | return std::invoke(std::forward<_Tp>(__g)._M_fd, | ~~~~~~~~~~~~~~~~~~~~~~~^~~~~ /opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:1023:33: note: candidates are: 'int (* std::_Bind_front<int (*)(int, B), int>::_M_fd)(int, B)' 1023 | [[no_unique_address]] _Fd _M_fd; | ^~~~~ /opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:1023:33: note: 'int (* std::_Bind_front<int (*)(int, A), int>::_M_fd)(int, A)' /opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:1019:53: error: request for member '_M_bound_args' is ambiguous 1019 | std::get<_Ind>(std::forward<_Tp>(__g)._M_bound_args)..., | ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~ /opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:1024:55: note: candidates are: 'std::tuple<int> std::_Bind_front<int (*)(int, B), int>::_M_bound_args' 1024 | [[no_unique_address]] std::tuple<_BoundArgs...> _M_bound_args; | ^~~~~~~~~~~~~ /opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:1024:55: note: 'std::tuple<int> std::_Bind_front<int (*)(int, A), int>::_M_bound_args' Compiler returned: 1 A smaller reproduction without including the stdlib is: $ cat <source> template <typename Fn> struct invoke_result { Fn fn; template <typename Self, typename CallArg> void operator()(this Self self, CallArg arg) requires requires {self.fn(arg);}{} }; template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; struct A{}; struct B{}; auto a = overloaded{invoke_result<int(*)(A)>(), invoke_result<int(*)(B)>(), }(A()); $ g++ -std=c++2b -fconcepts-diagnostics-depth=2 <source> <source>:15:11: error: no match for call to '(overloaded<invoke_result<int (*)(A)>, invoke_result<int (*)(B)> >) (A)' 13 | auto a = overloaded{invoke_result<int(*)(A)>(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 | invoke_result<int(*)(B)>(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 | }(A()); | ~^~~~~ <source>:6:3: note: candidate: 'template<class Self, class CallArg> void invoke_result<Fn>::operator()(this Self, CallArg) requires requires{invoke_result<Fn>::operator()::self.fn(invoke_result<Fn>::operator()::arg);} [with CallArg = Self; Fn = int (*)(A)]' 6 | operator()(this Self self, CallArg arg) | ^~~~~~~~ <source>:6:3: note: template argument deduction/substitution failed: <source>:6:3: note: constraints not satisfied <source>: In substitution of 'template<class Self, class CallArg> void invoke_result<int (*)(A)>::operator()(this Self, CallArg) requires requires{invoke_result<Fn>::operator()::self.fn(invoke_result<Fn>::operator()::arg);} [with Self = int (*)(A); CallArg = <missing>]': <source>:15:11: required from here 13 | auto a = overloaded{invoke_result<int(*)(A)>(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 | invoke_result<int(*)(B)>(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 | }(A()); | ~^~~~~ <source>:6:3: required by the constraints of 'template<class Fn> template<class Self, class CallArg> void invoke_result<Fn>::operator()(this Self, CallArg) requires requires{invoke_result<Fn>::operator()::self.fn(invoke_result<Fn>::operator()::arg);}' <source>:7:12: in requirements [with Self = overloaded<invoke_result<int (*)(A)>, invoke_result<int (*)(B)> >; CallArg = A] <source>:7:29: note: the required expression 'self.fn(arg)' is invalid, because 7 | requires requires {self.fn(arg);}{} | ~~~~~~~^~~~~ <source>:7:27: error: request for member 'fn' is ambiguous 7 | requires requires {self.fn(arg);}{} | ~~~~~^~ <source>:3:6: note: candidates are: 'int (* invoke_result<int (*)(B)>::fn)(B)' 3 | Fn fn; | ^~ <source>:3:6: note: 'int (* invoke_result<int (*)(A)>::fn)(A)' <source>:6:3: note: candidate: 'template<class Self, class CallArg> void invoke_result<Fn>::operator()(this Self, CallArg) requires requires{invoke_result<Fn>::operator()::self.fn(invoke_result<Fn>::operator()::arg);} [with CallArg = Self; Fn = int (*)(B)]' 6 | operator()(this Self self, CallArg arg) | ^~~~~~~~ <source>:6:3: note: template argument deduction/substitution failed: <source>:6:3: note: constraints not satisfied <source>: In substitution of 'template<class Self, class CallArg> void invoke_result<int (*)(B)>::operator()(this Self, CallArg) requires requires{invoke_result<Fn>::operator()::self.fn(invoke_result<Fn>::operator()::arg);} [with Self = int (*)(B); CallArg = <missing>]': <source>:15:11: required from here 13 | auto a = overloaded{invoke_result<int(*)(A)>(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 | invoke_result<int(*)(B)>(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 | }(A()); | ~^~~~~ <source>:6:3: required by the constraints of 'template<class Fn> template<class Self, class CallArg> void invoke_result<Fn>::operator()(this Self, CallArg) requires requires{invoke_result<Fn>::operator()::self.fn(invoke_result<Fn>::operator()::arg);}' <source>:7:12: in requirements [with Self = overloaded<invoke_result<int (*)(A)>, invoke_result<int (*)(B)> >; CallArg = A] <source>:7:29: note: the required expression 'self.fn(arg)' is invalid, because 7 | requires requires {self.fn(arg);}{} | ~~~~~~~^~~~~ <source>:7:27: error: request for member 'fn' is ambiguous 7 | requires requires {self.fn(arg);}{} | ~~~~~^~ <source>:3:6: note: candidates are: 'int (* invoke_result<int (*)(B)>::fn)(B)' 3 | Fn fn; | ^~ <source>:3:6: note: 'int (* invoke_result<int (*)(A)>::fn)(A)' Compiler returned: 1 The issue can be fixed by changing the definition of the operator() to: template <typename CallArg> void operator()(CallArg arg) requires requires {fn(arg);}{} This bug comes up relatively frequently in a codebase that uses `std::visit` in conjunction with `std::bind_front`, in perfectly innocent-looking code.