Reputation: 3583
The following code fails to compile (Godbolt link):
#include <concepts>
template <class Fn>
decltype(auto) g(Fn&& fn) { return fn(); }
template <typename T>
requires(std::integral<T>) int f() { return 0; }
template <typename T>
int f() { return 1; }
int main() {
f<int>();
f<void>();
g(f<int>); // error: invalid initialization of non-const reference of type 'int (&)()'
// from an rvalue of type '<unresolved overloaded function type>'
g(f<void>);
}
It seems unexpected to me that the overload resolution succeeds when calling f<int>()
(selecting the constrained version as a better match than the unconstrained version) but fails when passing f<int>
as an argument.
Note that changing the unconstrained version to a disjoint constraint does make it compile (Godbolt link):
#include <concepts>
template <class Fn>
decltype(auto) g(Fn&& fn) { return fn(); }
template <typename T>
requires(std::integral<T>) int f() { return 0; }
template <typename T>
requires(!std::integral<T>) int f() { return 1; }
int main() {
f<int>();
f<void>();
g(f<int>);
g(f<void>);
}
So is the compiler behavior correct? And if so, is this an inconsistency in the standard, or is it intended to work this way?
Upvotes: 11
Views: 326
Reputation: 39818
It seems that neither GCC nor Clang has fully implemented the rules for forming pointers to constrained functions: [over.over]/5 definitely considers constraint ordering in choosing an overload. There were some late changes to these, although they’re just as relevant to the disjoint-constraints case as to the unconstrained case.
Upvotes: 5