I really liked this question and the discussions around it, specially the solution from T.C (I don't have 50 points to comment there, so I'll post comments as another solution).
I just passed from a similar situation, where a pair concept is need, but also needed the library to work on both C++17 and C++20.
This solution from T.C. works for both c++17 and c++20.
template<class P>
concept bool Pair = requires(P p) {
typename P::first_type;
typename P::second_type;
p.first;
p.second;
requires my_same_as<decltype(p.first), typename P::first_type>;
requires my_same_as<decltype(p.second), typename P::second_type>;
};
where my_same_as is defined as std::same_as from c++20:
template<class Me, class Other>
concept bool my_same_as = std::is_same_v<Me, Other> && std::is_same_v<Other, Me>;
I've tried several "implementations of pairs" and interesting point is that fields first and second may vary from reference or not-reference types.
T.C. mentioned that we could replace fields with:
{ p.first } -> my_same_as<typename P::first_type&>;
{ p.second } -> my_same_as<typename P::second_type&>;
I found out that this only works on c++20, strangely not on c++17 (it compiles fine, but doesn't match concept!). Somehow it doesn't match either reference or non-reference (requiring complex implementation with an || and std::remove_reference_t).
One portable solution that I found for both c++17 and c++20 was:
template<typename P>
concept bool Pair = requires(P p)
{
typename P::first_type;
typename P::second_type;
{ p.first } -> my_convertible_to<typename P::first_type>;
{ p.second } -> my_convertible_to<typename P::second_type>;
};
where my_convertible_to is equivalent to std::convertible_to from c++20:
template <class From, class To>
concept bool my_convertible_to =
std::is_convertible_v<From, To> &&
requires(std::add_rvalue_reference_t<From> (&f)()) {
static_cast<To>(f());
};
I cannot explain why this subtle behavior changed from c++17 to c++20 (on is_same_v logic), but I'm posting here as it may help others in a similar situation. I used g++-8 for c++17 and g++-10.1 for c++20. Thanks for all the learning!