Подтвердить что ты не робот

Функция std:: не может отличить перегруженные функции

Я пытаюсь понять, почему 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)> может соответствовать только одной из этих функций, и все же он терпит неудачу. Почему это так? Я знаю, что могу обойти это, используя лямбда или указатель на функцию, а затем сохранит функцию функции в функции. Но почему это не удается? Разве не ясно, какая функция я хочу выбрать? Пожалуйста, помогите мне понять, почему это не удается, поскольку я не могу понять, почему в этом случае сбой шаблона не работает.

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

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.

EDIT. В ответ на вопрос 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 может быть инициализирован любым объектом, , поскольку его конструктор templatized, который не зависит от аргумента шаблона, который вы передали в std::function.

4b9b3361

Ответ 1

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

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

template<class F> function(F f);

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

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

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

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

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

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

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

Ответ 2

Хотя очевидно, что вы хотите, проблема в том, что 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)

Ответ 3

Try:

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

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

Ответ 4

Другой способ справиться с этим - с общей лямбдой в С++ 14:

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

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

Ответ 5

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

С++ 11 standard (20.8.11)

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

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

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

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