C++11 auto, std::function и неоднозначный вызов перегруженной функции
Мне интересно, если кто-нибудь знает, почему следующий пример не компилируется, давая неоднозначный вызов ошибки функции перегрузки. Если я заменю auto на строго типизированную сигнатуру функтора, он сможет правильно различать перегрузки двух методов.
Я заметил, что та же проблема не возникает, если не использовать std::function в качестве аргумента перегрузки. Если мои перегрузки принимают просто float и int, компилятор может правильно различать эти две перегрузки, даже когда используется ключевое слово auto для определения моих входных аргументов. Я компилирую это в VisualStudio 2012. Может ли это быть просто ошибкой в компиляторе VS? У меня нет доступа к машине с GCC или Clang прямо сейчас, но кто-нибудь знает, скомпилируется ли она там?
Ошибка компиляции: неоднозначный вызов функции перегрузки
class AmbiguousOverload
{
public:
static void OverloadedMethod(std::function<int()>) {}
static void OverloadedMethod(std::function<float()>) {}
};
int _tmain(int argc, _TCHAR* argv[])
{
auto func1 = []() -> float {
return 0.5f;
};
auto func2 = []() -> int {
return 12;
};
AmbiguousOverload::OverloadedMethod(func1);
AmbiguousOverload::OverloadedMethod(func2);
return 0;
}
Собирает
class AmbiguousOverload
{
public:
static void OverloadedMethod(std::function<int()>) {}
static void OverloadedMethod(std::function<float()>) {}
};
int _tmain(int argc, _TCHAR* argv[])
{
std::function<float()> func1 = []() -> float {
return 0.5f;
};
std::function<int()> func2 = []() -> int {
return 12;
};
AmbiguousOverload::OverloadedMethod(func1);
AmbiguousOverload::OverloadedMethod(func2);
return 0;
}
Также компилирует
class AmbiguousOverload
{
public:
static void OverloadedMethod(int) {}
static void OverloadedMethod(float) {}
};
int _tmain(int argc, _TCHAR* argv[])
{
auto v1 = 0.5f;
auto v2 = 12;
AmbiguousOverload::OverloadedMethod(v1);
AmbiguousOverload::OverloadedMethod(v2);
return 0;
}
1 ответ
std::function
имеет жадный template
конструктор, который попытается построить его из всего, что вы передали1. std::function
не очень подходит для использования в разрешении перегрузки. В одном случае это сработало, потому что идеальные совпадения предпочтительнее конверсии, но, как уже отмечалось, они хрупкие
Обратите внимание, что лямбды и std::function
объекты не связаны между собой. std::function
знает, как обернуть лямбду, но она может обернуть любой копируемый вызываемый объект. Лямбды - это автоматически созданные классы с анонимным именем, которые можно копировать и вызывать. std::function
является классом, предназначенным для стирания вызова типа
Представьте, если ваши переопределения заняли short
а также long
, auto x = 2.0
а также short s = 2.0
будет соответствовать auto x = lambda
а также std::function<blah> f = lambda
случаев. Когда вы выбираете тип явно, вы вызываете преобразование типа, и типы, которые вы выбрали явно, не имели двусмысленности. Но когда ты сделал auto
он взял настоящий тип - и настоящий тип был неоднозначным.
SFINAE или отправка тегов с использованием std::result_of
позволит вам обрабатывать эти переопределения вручную.
1 В определенной степени это недостаток в std::function
, Его "универсальный" конструктор должен действительно участвовать в разрешении перегрузки, только когда передаваемый тип является копируемым и std::result_of_t< X( Args... ) >
может быть преобразован в тип результата std::function
, Я подозреваю, что пост-концепции это будут добавлены в стандарт, так как пост-концепции это действительно легко и написать, и выразить (и при этом очень мало соответствующего кода будет нарушено). Однако в данном конкретном случае это на самом деле не поможет, так как int
а также float
могут быть преобразованы друг в друга, и нет никакого способа сказать "этот конструктор будет работать, но на самом деле это не предпочтительный вариант".