Skip to content

Re-implementing lookup function using same logic as in std::ranges

Comment raised on !4520 (comment 9998415) by @graven:

ChatGPT offers two suggestions, the simplest of which should be applicable here:

    concept HasFourMomentum =
      requires (T t) { fourMomentum(t); } ||        // ADL + normal unqualified lookup
      requires (T t) { LHCb::Event::fourMomentum(t); }; // explicit fallback

and a more complicated alternative, which is very interesting, but really suggests that the function lookup is completely re-implemented using the same style as used in std::ranges, using an explicit proper Customization Point Object (CPO) setup (see here, here). I'll only include it here for future reference - if we do this, it should be done as a dedicated MR across all accessor functions:

namespace detail {
  struct four_momentum_fn {
    template <class T>
    constexpr auto operator()(T&& t) const
      noexcept(noexcept(fourMomentum(std::forward<T>(t))))
      -> decltype(      fourMomentum(std::forward<T>(t)))
    {
      using SomeNamespace::fourMomentum;  // bring fallback into scope
      return fourMomentum(std::forward<T>(t)); // unqualified: ADL + using
    }
  };

  inline constexpr four_momentum_fn four_momentum{};
}

template <typename T>
concept HasFourMomentum = requires (T t) { detail::four_momentum(t); };

// …and when you need to call it:
auto p = detail::four_momentum(obj);

which would imply that every time an ADL function is invoked and which follows the pattern using LHCb::Event::SomeFun; SomeFun(x) will have to be changed to LHCb::Event::SomeFun(x), where LHCb::Event::SomeFun is no longer a function, but the instance of an object which calls the relevant function which 'centralizes' the using and unqualified call, and also figures out any alternatives to call (eg. if a member function exists, call it)...