maciekp
maciekp

Reputation: 73

Template functor cannot deduce reference type

I've got a functor f, which takes a function func and a parameter t of the same type as func. I cannot pass g to f because of compilation error (no matching function for call to f(int&, void (&)(int&)) ). If g would take non-reference parameter g(int s), compilation finishes. Or if I manually specify template parameter f<int&>(i, g), compilation also finishes.

template<typename T>
void f(T t, void (*func)(T)) {}

void g(int& s) {}

int main(int, char*[])
{
    int i = 7;

    f(i, g); // compilation error here

    return 0;
}

How can I get deduction to work?

Upvotes: 4

Views: 1489

Answers (4)

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 507085

The problem is that if in a template one of its function parameters is not a reference type before deduction starts, that parameter will never deduce to a reference type. So in the deduction on the left side, T yields int, but on the deduction on the right side, T yields int&. That's a mistmatch and the compiler complains.

The best is to make the function parameter the same type as the parameter type of the function pointer:

template<typename T> struct identity { typedef T type; };

template<typename T>
void f(typename identity<T>::type t, void (*func)(T)) {}

By using identity<T>::type, you disable deduction on the left side. Once the T was determined at the right side, T is sustituted into the left side and yields the final parameter type.

One guy proposed to take the right side as a template parameter - this is a good thing since it can then accept function objects with operator() overloaded. But you then face the problem of having to know whether it wants a reference or not. To solve it, boost has reference_wrapper (by the way, boost also has the identity template above).

template<typename T, typename F>
void f(T t, F func) {}

Now, if you want to pass a reference and not a copy, you can do that like this

int i;
f(boost::ref(i), some_function);

ref returns a reference_wrapper object which is implicitly convertible to T&. So if you call func(t), the t is converted to the target reference automagically. If you don't want to pass a reference, just pass i directly.

Upvotes: 5

Michael Kristofik
Michael Kristofik

Reputation: 35188

template<typename T>
void f(T t, void (*func)(T)) {}

The key thing here is that you used T in both arguments. That means the types have to match exactly.

void g(int& s) {}

int i = 7;
f(i, g);

In your code, you pass an int and a function taking an int& to f(). Those are different types but your template for f expects two of the same type. The easiest fix, as others have suggested, is to make the function a template as well.

template <typename T, typename F>
void f(T t, F func) 
{ 
    func(t);
}

Upvotes: 1

UncleBens
UncleBens

Reputation: 41341

You can invoke the function like this:

f<int&>(i, g);

But now i will be passed by reference too.

In general, I'd make the function a template type too:

template <typename T, typename F>
void f(T t, F func) 
{ 
    func(t); //e.g
}

Upvotes: 6

Charles Beattie
Charles Beattie

Reputation: 5949

I think you need either:

void f(T t, void (*func)(T&)) {}

or:

void g(int s) {}

but I prefer:

template<typename T, typename T2> 
void f(T t, T2 func) {}

as this will work with functions and functors.

Upvotes: 5

Related Questions