https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102670
Bug ID: 102670 Summary: Erroneous "missing template arguments" message for wrapper of ADL function template Product: gcc Version: 12.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: friedkeenan at protonmail dot com Target Milestone: --- With the following code, GCC outputs an erroneous error message of "missing template arguments": #include <utility> namespace ns { struct S { }; template<int I> constexpr int adl(const S &) { return I; } } namespace redirect { template<typename T, int I> concept can_call_adl = requires(T &&t) { adl<I>(std::forward<T>(t)); }; template<int I> struct adl_fn { template<can_call_adl<I> T> constexpr decltype(auto) operator ()(T &&t) const { return adl<I>(std::forward<T>(t)); } }; namespace { template<int I> constexpr inline adl_fn<I> adl{}; } } static_assert(redirect::can_call_adl<ns::S, 3>); int main() { // return adl<3>(ns::S{}); return redirect::adl<3>(ns::S{}); } Godbolt link: https://godbolt.org/z/or5n8EM6q As you can see, even though ns::S satisfies the redirect::can_call_adl concept, when the templated call operator is instantiated, it thinks the code is invalid. If you remove the indirection, no error message is presented and everything works as expected. Additionally, Clang handles this code just fine. If you however use the following code, everything works fine: #include <utility> namespace ns { struct S { }; template<int I> constexpr int adl(const S &) { return I; } } namespace redirect { namespace _adl { /* Poison pill. */ template<int I> void adl() = delete; template<typename T, int I> concept can_call_adl = requires(T &&t) { adl<I>(std::forward<T>(t)); }; template<int I> struct adl_fn { template<can_call_adl<I> T> constexpr decltype(auto) operator ()(T &&t) const { return adl<I>(std::forward<T>(t)); } }; } namespace { template<int I> constexpr inline _adl::adl_fn<I> adl{}; } } static_assert(redirect::_adl::can_call_adl<ns::S, 3>); int main() { // return adl<3>(ns::S{}); return redirect::adl<3>(ns::S{}); } Godbolt link: https://godbolt.org/z/jocaKrjbW This sort of functionality is desirable for making a wrapper that handles both ADL-discovered `get` functions and `get` member functions (as structured binding allows both), similar to the std::ranges::begin etc. wrappers.