[Bug c++/102670] New: Erroneous "missing template arguments" message for wrapper of ADL function template

friedkeenan at protonmail dot com gcc-bugzilla@gcc.gnu.org
Sat Oct 9 18:51:32 GMT 2021


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.


More information about the Gcc-bugs mailing list