views:

318

answers:

4

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?

+5  A: 

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.

Charles Beattie
+6  A: 

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
}
UncleBens
Works for me, thanks!
maciekp
+1  A: 
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);
}
Kristo
+4  A: 

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.

Johannes Schaub - litb
Hm, the `identity` version breaks with `const int i` too.
Georg Fritzsche
@gf can you please show an example? I'm not sure what you mean and where it breaks.
Johannes Schaub - litb
Georg Fritzsche
@gf i think this is very much the intended behavior, though. Suppose `f` would accept it merely by its parameter. But that won't do any good, because we will still get an error when it calls `func(t)`. `f` does well with rejecting that upfront, unless i'm missing something :)
Johannes Schaub - litb
Euh, yes ... i am going to catch up on sleep now. Sorry for the confusion :)
Georg Fritzsche