Перегрузка функции с использованием лямбда-функции

Рассмотрим следующий пример

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.

Вместо этого вы можете использовать более продвинутые механизмы, чтобы сделать его более общим (т.е. расширение переменных аргументов в аргументах), но я хотел показать, как работает основной механизм.