Ответ 1
Имя функции не является именем объекта 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 аргументом", "можно вызвать с двумя аргументами", а затем указать, что аргумент 1 аргумента разрешен только тогда, когда вы можете вызывать с 1, но не с двумя аргументами,
Существуют также методы упорядочения порядка перегрузки с помощью меток.