Перегрузка функции с использованием лямбда-функции
Рассмотрим следующий пример
void foo(const std::function<int()>& f) {
std::cout << f() << std::endl;
}
void foo(const std::function<int(int x)>& f) {
std::cout << f(5) << std::endl;
}
int main() {
foo([](){return 3;});
foo([](int x){return x;});
}
Это не скомпилируется, потому что вызов foo
называется неоднозначным. Насколько я понимаю, это связано с тем, что функция лямбда не является априорной a std::function
, но должна быть отброшена на нее и что существует конструктор std::function
, который принимает произвольный аргумент.
Может кто-то может объяснить мне, почему кто-то создал неявный конструктор, который принимает произвольный аргумент. Однако мой вопрос заключается в том, существует ли обходное решение, которое позволяет использовать сигнатуру функции лямбда-функций для перегрузки функции foo
. Я пробовал указатели функций, но это не сработало, потому что захват лямбда-функций не может быть применен к нормальному указателю функции.
Любая помощь приветствуется.
Ответы
Ответ 1
Ваш компилятор корректен в соответствии с С++ 11. В С++ 14 добавляется правило, в котором говорится, что шаблон конструктора не должен участвовать в разрешении перегрузки, если только тип аргумента не может быть вызван с помощью типов аргументов std::function
. Поэтому этот код должен компилироваться в С++ 14, но не в С++ 11. Рассмотрите это как недосмотр в С++ 11.
Теперь вы можете обойти это путем явного преобразования:
foo(std::function<int()>([](){return 3;}));
Ответ 2
http://coliru.stacked-crooked.com/a/26bd4c7e9b88bbd0
Альтернативой использованию std:: function является использование шаблонов. Шаблоны позволяют избежать накладных расходов памяти, связанных с std:: function. Механизм вычитания типа шаблона выведет правильный тип лямбды, пройденный, так что приведение сайта сайта удаляется. Однако у вас все еще есть неоднозначность перегрузки для аргумента no-args vs args.
Вы можете сделать это, используя трюк с возвращаемыми типами возврата, которые ведут себя подобно enable_if.
template<typename Callable>
auto baz(Callable c)
-> decltype(c(5), void())
{
std::cout << c(5) << std::endl;
}
Вышеперечисленная перегрузка baz будет только допустимым кандидатом на перегрузку, когда параметр шаблона Callable может быть вызван с аргументом 5.
Вместо этого вы можете использовать более продвинутые механизмы, чтобы сделать его более общим (т.е. расширение переменных аргументов в аргументах), но я хотел показать, как работает основной механизм.