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 могут быть преобразованы друг в друга, и нет никакого способа сказать "этот конструктор будет работать, но на самом деле это не предпочтительный вариант".

Другие вопросы по тегам