Chris
Chris

Reputation: 615

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.

Upvotes: 1

Views: 122

Answers (2)

David Lichmann
David Lichmann

Reputation: 1506

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.

Upvotes: 2

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275936

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.

Upvotes: 2

Related Questions