Перегрузка аргумента std:: function для соответствия lambda

Возможный дубликат:
Disambiguating вызовы функций, принимающих std:: functions
Не является ли аргумент шаблона (подпись) части std:: function его типа?

Я хочу перегрузить функцию, чтобы она могла быть вызвана с различными различными лямбдами (как правило, с более или менее аргументами). Очевидная вещь, которую я пробовал:

#include <functional>
#include <iostream>

extern void fn(std::function<void(int)>);
extern void fn(std::function<void(int, int)>);

void test()
{
    fn([](int a) { std::cout << "lambda with 1 arg " << a << std::endl; });
}

Однако это не с g++ (проверено v4.6.2 и v4.7.1) с ошибкой:

test.cc: In function ‘void test()’:
test.cc:9:74: error: call of overloaded ‘fn(test()::<lambda(int)>)’ is ambiguous
test.cc:9:74: note: candidates are:
test.cc:4:13: note: void fn(std::function<void(int)>)
test.cc:5:13: note: void fn(std::function<void(int, int)>)

Теперь я нашел альтернативный (и гораздо более сложный) подход здесь и здесь, но мой вопрос является, почему приведенный выше код не работает? Есть ли что-то в стандарте, в котором говорится, что он не может работать, или это просто ошибка/ограничение g++?

Ответ 1

Каждая Lambda [](int a) { std::cout << "lambda with 1 arg " << a << std::endl; } имеет уникальный тип, даже другой лямбда, такой же, как указано выше, приведет к разному лямбда-типу с элементом operator()(int a)

В вашей реализации std::function есть шаблонное преобразование, которое может использоваться как std::function<void(int)>, так и std::function<void(int, int)>. Хотя только один из них компилируется при создании экземпляра, они оба рассматриваются для разрешения перегрузки, и это создает неоднозначность. Чтобы получить желаемый результат, библиотеке необходимо использовать SFINAE, чтобы исключить ошибочный из набора кандидатов перегрузки (это делают последние версии libС++).

Ответ 2

Вопрос звучит наизнанку. Вы определяете тип с помощью std::function, чтобы описать, как вы собираетесь называть объекты этого типа и каково их возвращаемое значение. Затем вы можете использовать эту специализацию std::function для переноса различных вызываемых объектов, включая lambdas, которые имеют разные типы аргументов или другой тип возвращаемого значения.