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?
g(static_cast<int(*)()>(f<int>));
example. I'm not sure why that seems to help though. Edit: Oups, it only worked in clang++, not g++. – Firebratint (*x)() = &f<int>;
fails with the same error (on g++, as OP's godbolt link uses). – Caitling(f<int>)
– Firebrat