Имя функции не является именем объекта C ++.
Вместо этого, когда вы используете имя функции, происходит множество преобразований. Разрешение перегрузки выполняется на основе вызывающего или (неявного или явного) контекста приведения, и создается указатель.
Аргументы по умолчанию для функции являются частью разрешения перегрузки. Они никогда не передаются как часть типа указателя на функцию.
Вы можете создать простую оболочку, которая превращает имя функции в объект функции:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define OVERLOADS_OF(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__( decltype(args)(args)... ) )
с этим вы можете изменить свой код:
return func(OVERLOADS_OF(defaultFree));
и получить аргументы по умолчанию, которые func
и SFINAE должны учитывать, чтобы привести к неоднозначности.
Теперь OVERLOADS_OF(defaultFree)
- это объект функции, который SFINAE проверяет, могут ли его аргументы передаваться вызываемому элементу defaultFree
. Это позволяет передать 1 аргумент.
Объекты функций не являются функциями. Лямбда - это объект функции, так же как и тип возвращаемого значения OVERLOADS_OF
. Объекты функций могут быть переданы и их перегружены operator()
; они могут запомнить свои параметры по умолчанию, выполнить SFINAE и т. д.
Так что, когда вы проходите лямбду, обе возможности законны. Когда вы передаете функцию, она становится указателем на вызов функции без аргумента по умолчанию, и это однозначно не принимает 1 параметр.
Чтобы решить вашу проблему, вам нужно, чтобы одна перегрузка выглядела лучше, чем другая.
Один из подходов заключается в использовании ...
:
namespace impl {
template <typename F>
auto func(F f,...) -> decltype(f(42))
{
int a = 51;
return f(51);
}
template <typename F>
auto func(F f, int) -> decltype(f(42, 42))
{
int a = 0;
int b = 10;
return f(a, b);
}
}
template <typename F>
auto func(F f) -> decltype( impl::func(f, 0) )
{
return impl::func(f, 0);
}
Хитрость в том, что int
предпочтительнее, чем ...
, когда вы передаете 0
.
Вы также можете быть более явным и генерировать такие черты, как «можно вызвать с 1 аргументом», «можно вызвать с 2 аргументами», а затем указать, что регистр с 1 аргументом разрешен только тогда, когда вы можете вызвать с 1, но не 2 аргумента.
Существуют также методы упорядочения разрешения перегрузки тегами.