Passing a concept-constrained function overload
Asked Answered
G

1

11

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?

Gamma answered 5/4, 2022 at 17:51 Comment(3)
I'm not sure if the compilers are correct or not, but another workaround could be to add a cast: 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++.Firebrat
@TedLyngmo That doesn't seem to work, the ambiguity is at the point of conversion and not related to parameter passing. int (*x)() = &f<int>; fails with the same error (on g++, as OP's godbolt link uses).Caitlin
@BenVoigt Yes, I noticed when I changed from clang++ to g++. Odd that clang++ accepts that cast when it doesn't accept plain g(f<int>)Firebrat
I
5

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.

Inventor answered 5/4, 2022 at 18:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.