The following isn't correct since it can't differentiate an ambiguous overload resolution from a deleted overload result. And I can't think of a way to differentiate that.
I'll leave my previous answer up for reference below.
Maybe something like this (https://godbolt.org/z/hTsq5rYnq):
namespace foo_is_deleted_impl {
template<typename...>
void foo(...);
}
template<typename... Args>
inline constexpr auto foo_is_deleted = []{
auto a = requires { foo(std::declval<Args>()...); };
using namespace foo_is_deleted_impl;
auto b = requires { foo(std::declval<Args>()...); };
return !(a || b);
}();
The idea is to first test whether overload resolution succeeds with a usable candidate (meaning non-deleted) for a
.
Then I make the overload foo_is_deleted_impl::foo
visible to unqualified name lookup inside the lambda with using namespace
and repeat the test for b
. The overload is declared in such a way that I think it is impossible for it to be better candidate than any other overload (If someone spots a case I missed, let me know).
If a
is true, then a deleted overload surely wasn't taken, so I return false
.
If a
is not true, but b
is, then overload resolution must have had failed for a
, because the overload chosen for b
wouldn't be better than an overload chosen for a
, and so again false
is returned.
If both a
and b
are false, then there are two possibilities: Either b
failed because one of the non-foo_is_deleted_impl::foo
overloads was chosen and is deleted, in which case I return true
, or it failed because overload resolution became ambiguous with foo_is_deleted_impl::foo
. That means however that overload resolution for a
did find a viable candidate and so its result should be returned.
For all of this it is important that relative to one-another with respect to scope foo
, foo_is_deleted_impl
and foo_is_deleted
are placed as they are. The placement of foo_is_deleted
below the declarations of foo
is also important because only ADL will lookup from the point of instantiation.
This also assumes that foo
is an overload set of free function (templates) called with unqualified name. It should work with ADL.