简体   繁体   中英

Passing a function with templated parameters as an argument, with function pointers vs. std::function

So I have some generic class with two functions that take functions as parameters. One takes a function pointer and one takes a std::function. Both have a template parameter.

#include <functional>
#include <memory>

namespace {
    void example(const std::shared_ptr<const int>&) {}
}

class Generic {
public:
    Generic() {}
    virtual ~Generic() {}
    template <typename Targ>
    void doWork(std::function<void(const std::shared_ptr<const Targ>&)> arg) {}

    template <typename Targ>
    void doWork2(void(*function)(const std::shared_ptr<const Targ>&)) {}
};

class Special : public Generic {
public:
    Special() {
        //doWork(&example);   // Fail!
        doWork<int>(&example);  // OK!
        std::function<void(const std::shared_ptr<const int>&)> func = &example;
        doWork(func); // OK!
        doWork2(&example);  // OK!
    }
};

int main(int argc, char** argv) {
    Special special;
    return 0;
}

With the function pointer it compiles, but with the std::function it does not. Why does template deduction fail here?

Clang reports:

example.cpp:27:9: error: no matching member function for call to 'doWork'
        doWork(&example);
        ^~~~~~
example.cpp:14:10: note: candidate template ignored: could not match 'function<void (const shared_ptr<const type-parameter-0-0> &)>' against 'void (*)(const std::shared_ptr<const int> &)'
    void doWork(std::function<void(const std::shared_ptr<const Targ>&)> arg) {
         ^
1 error generated.

This happens because the constructor can't deduce the type of its class. If my wording sounds weird, perhaps this example will help:

template <class T>
class Example {
    Example(const T&) { /*...*/  }
};

If I have a function template, such as template <class T> void f(const Example<T>&) , I can't just do f(10) . This is what your code boils down to. std::function can't know its template parameters based on what you passed to its (non-explicit) constructor.

Note: this is, by the way, in the works for C++17.

Template argument deduction does not work like that.

Template argument deduction is a pattern match. Is example an object of type std::function<void(const std::shared_ptr<const Targ>&)> for some type Targ ?

Not convertible-to, but actually an object of that type already?

No, it is not.

It is, however, already a function pointer (using the implicit decay rules).

There is a C++17 feature that involve deducing template arguments from constructor types; std::function may or may not be instrumented to learn its own type from a function pointer when C++17 or C++20 comes out in this situation. I lack expertise in C++17 to be certain.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM