Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::function template argument resolution

I am currently working on a library where I am chaining function objects.

I am creating a function template that takes a callable object (std::function at the moment) and is parametrized on the output and input type of the function. Here is a simplified version of what I am defining:

template <typename In, typename Out>
std::vector<Out> process(std::vector<In> vals, std::function< Out(In) > func)
{
    // apply func for each value in vals
    return result;
}

The problem I am having is on usage. It seems that when I pass a lambda, the compiler cannot deduce the type correctly, so complains that the function doesn't exist:

std::vector<string> strings;
// does NOT compile
auto chars = process(strings,
        []( std::string s ) -> char
        {
            return s[0]; // return first char
        }
);

If I explicitly wrap the lambda in std::function, the program compiles:

std::vector<string> strings;
// DOES compile
auto chars = process(strings,
        std::function< char(std::string) >(
        []( std::string s ) -> char
        {
            return s[0]; // return first char
        })
);

I haven't tested passing function pointers or function objects yet, but it seems like it will be difficult for the compiler to deduce the the In and Out parameters if I'm not directly passing the explicit std::function object.

My question is this: is there a way to get around this, so that I can deduce the input/return type of a callable object without explicitly mentioning them at the call site?

Perhaps parametrize the template on the function type instead of the input/return types? Essentially I need to deduce the In and Out type for an arbitrary callable. Perhaps some kind of auto/decltype trick for the return type of the template function?

Thank you.

like image 610
Alexander Kondratskiy Avatar asked Dec 06 '12 22:12

Alexander Kondratskiy


People also ask

What is the validity of template parameters?

3. What is the validity of template parameters? Explanation: Template parameters are valid inside a block only i.e. they have block scope.

What is template argument deduction in C++?

Class Template Argument Deduction (CTAD) is a C++17 Core Language feature that reduces code verbosity. C++17's Standard Library also supports CTAD, so after upgrading your toolset, you can take advantage of this new feature when using STL types like std::pair and std::vector.

What is the difference between the function template and template function?

Function Template is the correct terminology (a template to instantiate functions from). Template Function is a colloquial synonym. So, there's no difference whatsoever.

What is the difference between typename and class in template?

There is no difference between using <typename T> OR <class T> ; i.e. it is a convention used by C++ programmers.


2 Answers

I think what you can do is to create an intermediate return type deducing function which uses decltype to determine the arguments to be passed to the actual function object:

template <typename Out, typename In>
std::vector<Out> process_intern(std::vector<In> vals, std::function< Out(In) > func)
{
    // whatever
}

template <typename In, typename Func>
auto process(std::vector<In> vals, Func func) -> std::vector<decltype(func(vals[0]))>
{
    return process_intern<decltype(func(vals[0]))>(vals, func);
}

Of course, you may want to consider implementing the logic in process() directly anyway unless there is a reason to type erase the function type.

like image 193
Dietmar Kühl Avatar answered Oct 16 '22 17:10

Dietmar Kühl


is there a way to get around this, so that I can deduce the input/return type of a callable object without explicitly mentioning them at the call site?

No. User-defined conversions are not considered in template argument deduction. The compiler have to come up with In and Out such that the type of the parameter and the type of the argument have to match (almost) exactly, but they never will in this case.

Perhaps parametrize the template on the function type instead of the input/return types

Yes, that's what is normally done (take a look at the standard library algorithms, for example)

like image 30
Cubbi Avatar answered Oct 16 '22 17:10

Cubbi