I'm currently trying to understand the std::execution
proposal by studying libunifex's implementation. The library makes heavy use of template metaprogramming.
There's one piece of code I really have trouble understanding:
template <class Member, class Self>
Member Self::* _memptr(const Self&);
template <typename Self, typename Member>
using member_t = decltype(
(std::declval<Self&&>() .*
_memptr<Member>(std::declval<Self&&>())));
What exactly is member_t
used for? It's used in the implementation of then
:
template <typename Sender, typename Receiver>
requires std::same_as<std::remove_cvref_t<Sender>, type> &&
receiver<Receiver> &&
sender_to<member_t<Sender, Predecessor>, receiver_t<std::remove_cvref_t<Receiver>>>
friend auto tag_invoke(tag_t<connect>, Sender&& s, Receiver&& r)
noexcept(
std::is_nothrow_constructible_v<std::remove_cvref_t<Receiver>, Receiver> &&
std::is_nothrow_constructible_v<Function, member_t<Sender, Function>> &&
is_nothrow_connectable_v<member_t<Sender, Predecessor>, receiver_t<std::remove_cvref_t<Receiver>>>
// ----------------------^ here
)
-> connect_result_t<member_t<Sender, Predecessor>, receiver_t<std::remove_cvref_t<Receiver>>> { /* ... */ }