ZivS ZivS - 1 month ago 6
C++ Question

C++11 function signature with variadic std::function

I'm trying to implement a function that takes an

std::function
which returns an it and might get any number of parameters.

I've tried the following but it doesn't compile and I can't understand what the error means.

template <typename ...Args>
void ThrowOnError(std::function<int(Args...)> func, Args... args)
{
int err = func(args...);
if (err < 0)
throw std::exception();
}

int f()
{
return 42;
}

int f(int x)
{
return x;
}


int main()
{
ThrowOnError(f);
ThrowOnError(f, 1);
}


I tried moving the templated function to header but it didn't work, also if I comment out the
f(int x)
function and only leave the call with only
f
, I still get a
no matching overloaded function found
and
'void ThrowOnError(std::function<int(Args...)>,Args...)': could not deduce template argument for 'std::function<int(Args...)>' from 'int (int)'


What is the problem here? what am I missing in the function?

P.S - I would like an answer which takes an
std::function
if possible, and not add another typename for the functor type.

Answer

You are not providing a std::function as a parameter and deduction can't take place.
In other there, to assign the function to the std::function, you must know the actual type. To find it, deduction must happen. For deduction to happen you should first assign the function pointer to the std::function, but the type of the latter is unknown (because deduction didn't take place yet).
So on in a loop.

Moreover, when you do this:

ThrowOnError(f);

It's impossible for the compiler to know what f you want to use.
You should rather do something like this:

ThrowOnError(std::function<int()>{static_cast<int(*)()>(f)});

Or this (if you accept to use another template parameter for the functor):

ThrowOnError(static_cast<int(*)()>(f));

This way you are picking the right function up from the overload set explicitly and the compiler has not to guess your intentions.
As mentioned, the latter would work fine if you accept to modify also the ThrowOnError function as it follows:

template <typename F, typename ...Args>
void ThrowOnError(F func, Args... args)
{
    int err = func(args...);
    if (err < 0)
        throw std::exception();
}

Or even better:

template <typename F, typename ...Args>
void ThrowOnError(F &&func, Args&&... args)
{
    int err = std::forward<F>(func)(std::forward<Args>(args)...);
    if (err < 0)
        throw std::exception();
}