I read about that C++20's constraint partial order solves the old problem of overloading a generic function for a specific struct and any of its derived structs, and is testing it out. Namely, the following code:
#include <concepts>
struct A {};
struct B : A {};
template <typename T>
void f(T&) {}
template <typename T> requires std::derived_from<T, A>
void f(T&) {}
int main() {
B b;
f(b);
return 0;
}
As tested on godbolt.org with x86-64 gcc 10.2, the call f(b) successfully invokes the second implementation, while the old way of writing void f(A&) {} fails to compete with the generic overload. (I am aware that in older standards of C++ there are also ways like choice tags or SFINAE on both functions to do it properly.)
However, if I changed the function definitions to the following:
template <typename T>
void f(T) {}
template <typename T> requires std::derived_from<T, A>
void f(T&) {}
(where one of the signatures is with reference and the other without; same is true if the two are switched), then the trick stops working and gcc reports that the call f(b) is ambiguous. I wish to know whether this is a behaviour specified in the C++20 standard (and how this is explained), and if I do intend to implement such overloads (perhaps where the generic overload copies everything by value while for a specific class of objects I wish to perform exactly the same operations using references -- I know this is a rare case, but just in case this is needed), is there any other tricks with constraints that help elegantly, or that we need to go back to the old methods of SFINAE?