std::function не распознает перегруженные функции

Я пытаюсь понять почему std::function не умеет различать перегруженные функции.

#include <functional>

void add(int,int){}

class A {};

void add (A, A){}

int main(){
        std::function <void(int, int)> func = add;
}

В коде, показанном выше, function<void(int, int)> может соответствовать только одной из этих функций, но она не работает. Почему это так? Я знаю, что могу обойти это, используя лямбда или указатель функции на фактическую функцию и затем сохраняя указатель функции в функции. Но почему это не удается? Разве из контекста не ясно, какую функцию я хочу выбрать? Пожалуйста, помогите мне понять, почему это не удается, поскольку я не могу понять, почему в этом случае не удается сопоставить шаблон.

Ошибки компиляции, которые я получаю на Clang для этого, следующие:

test.cpp:10:33: error: no viable conversion from '<overloaded function type>' to
      'std::function<void (int, int)>'
        std::function <void(int, int)> func = add;
                                       ^      ~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1266:31: note: 
      candidate constructor not viable: no overload of 'add' matching
      'std::__1::nullptr_t' for 1st argument
    _LIBCPP_INLINE_VISIBILITY function(nullptr_t) : __f_(0) {}
                              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1267:5: note: 
      candidate constructor not viable: no overload of 'add' matching 'const
      std::__1::function<void (int, int)> &' for 1st argument
    function(const function&);
    ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1269:7: note: 
      candidate template ignored: couldn't infer template argument '_Fp'
      function(_Fp,
      ^
1 error generated.

РЕДАКТИРОВАТЬ - В дополнение к ответу MSalters, я провел поиск на этом форуме и нашел точную причину, почему это не удается. Я получил ответ от ответа Наваза в этом посте.

У меня есть копия, вставленная из его ответа здесь:

    int test(const std::string&) {
        return 0;
    }

    int test(const std::string*) {
        return 0;
    }

    typedef int (*funtype)(const std::string&);

    funtype fun = test; //no cast required now!
    std::function<int(const std::string&)> func = fun; //no cast!

Так почему std::function<int(const std::string&)> не работает так funtype fun = test работает выше?

Ну, ответ, потому что std::function может быть инициализирован любым объектом, так как его конструктор является шаблоном, который не зависит от аргумента шаблона, который вы передали std::function ,

5 ответов

Решение

Для нас очевидно, какую функцию вы намерены выбрать, но компилятор должен следовать правилам C++, а не использовать умные скачки логики (или даже не такие умные, как в простых случаях, подобных этому!)

Соответствующий конструктор std::function является:

template<class F> function(F f);

который является шаблоном, который принимает любой тип.

Стандарт C++14 действительно ограничивает шаблон (начиная с LWG DR 2132), чтобы он:

не должны участвовать в разрешении перегрузки, если только f Callable (20.9.12.2) для типов аргументов ArgTypes... и тип возврата R,

это означает, что компилятор будет позволять вызывать конструктор только когда Functor совместим с подписью вызова std::function (который void(int, int) в твоем примере). В теории это должно означать, что void add(A, A) не является жизнеспособным аргументом и поэтому "очевидно" вы намеревались использовать void add(int, int),

Тем не менее, компилятор не может проверить "f Callable для типов аргументов...", пока не будет определен тип fЭто означает, что он должен быть уже неоднозначен между void add(int, int) а также void add(A, A) прежде чем он сможет применить ограничение, которое позволит ему отклонить одну из этих функций!

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

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

Хотя очевидно, что вы хотите, проблема в том, что std::function не может влиять на разрешение перегрузки &add, Если вы должны были инициализировать необработанный указатель на функцию (void (*func)(int,int) = &add), это работает. Это потому, что инициализация указателя на функцию - это контекст, в котором выполняется разрешение перегрузки. Тип цели точно известен. Но std::function примет почти любой аргумент, который можно вызвать. Такая гибкость в принятии аргументов означает, что вы не можете сделать разрешение перегрузки на &add, Множественные перегрузки add может подойти.

Явное приведение будет работать, т.е. static_cast<void(*)(int, int)> (&add),

Это может быть завернуто в template<typename F> std::function<F> make_function(F*) что позволит вам написать auto func = make_function<int(int,int)> (&add)

Пытаться:

std::function <void(int, int)> func = static_cast<void(*)(int, int)> (add);

Адреса к void add(A, A) а также void add(int, int) безусловно отличается. Когда вы указываете на функцию по имени, компилятору практически невозможно узнать, какой адрес функции вам нужен. void(int, int) Здесь нет ни намека.

Еще один способ справиться с этим - использовать общую лямбду в C++14:

int main() {
    std::function <void(int, int)> func = [](auto &&... args) { add(std::forward<decltype(args)>(args)...);
}

Это создаст лямбда-функцию, которая разрешит вещи без двусмысленности. Я не выдвигал аргументов,

Насколько я вижу, это проблема Visual Studio.

стандарт C++ 11 (20.8.11)

std::function synopsis
template<class R, class... ArgTypes> class function<R(ArgTypes...)>;

но VisualStudio не имеет этой специализации

clang ++ и g ++ прекрасно работают с перегрузкой std::functions

предыдущие ответы объясняют, почему VS не работает, но они не упоминали, что это ошибка VS

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