Explanation:
CLion and it's standard compiler give me an error that the "candidate template [is] ignored", when I write a lambda as parameter for a generic function that takes a lambda as argument.
This lambda takes a generic type T
and returns another unknown type A
.
The container class that I am writing is supposed to support functional operations like these in Scala or the ones from the Java Stream API.
To be exact:
The map function makes huge problems. It is implemented as a member function in a class called Sequence, which takes a generic parameter T
.
It is supposed to take an element of an already known type T
( actually it iterates through the whole sequence ) and convert it into an unknown type A
.
The implementation itself is not the problem, but I cannot call the function with the lambda syntax I know.
Code:
Sequence.h
template< typename T >
class Sequence {
public:
template< typename A >
auto map( std::function< A( const T ) > function ) const {
auto sequence = new Sequence< A >;
for ( const T& element : *this ) {
sequence->push( function( element ) );
}
return *sequence;
}
}
main.cpp
int main() {
Sequence< uint32_t > a;
a.push( 20 );
a.push( 30 );
a.push( 40 );
a.map( []( uint32_t c ) -> uint32_t {
return c * c;
} );
return 0;
}
As far as I understand a lambda gets initialized, which
takes a parameter of type std::uint32_t
and returns a value of type std::uint32_t
.
The generic parameter A
doesn't seem to get inferred at this point.
Error Stack:
main.cpp:21:7: error: no matching function for call to 'Sequence<unsigned int>::map(main()::<lambda(uint32_t)>)'
} );
Sequence.h:143:10: note: candidate: template<class A> auto Sequence<T>::map(std::function<A(T)>) const [with A = A; T = unsigned int]
auto map( std::function< A( const T ) > function ) const {
note: template argument deduction/substitution failed:
main.cpp:21:7: note: 'main()::<lambda(uint32_t)>' is not derived from 'std::function<A(unsigned int)>'
} );
Thanks in advance!
As already pointed out by @cpplearner in the comments above, the reason why this won't work is explained in detail here: Constructing std::function argument from lambda.
If your Sequence is already a template, there should be no reason to require the callable for your
map
function to be passed in the form of anstd::function
. At least, I seem unable to come up with a reason that could justify doing this.std::function
is generally not free to construct or call, and also can inhibit inlining. Best avoid it unless you really need the capability to store arbitrary callables for later use. Simply take a forwarding reference, e.g.:You should be able to deduce the result type of whatever invoking
f
on an element of your sequence ends up producing, e.g., viaFurthermore, don't just return a raw pointer to a new
Sequence
. Use anstd::unique_ptr
.Putting it all together, your
map
function could look something like this:Ignoring the
const
problem, you have a sort of chicken-and-egg problem.It's true that your lambda can be converted to a
std::function<std::uint32_t(std::unit32_t)>
.But it's also true that the lambda isn't a
std::function<std::uint32_t(std::unit32_t)>
so the compiler can't deduceA
.And if the compiler can't deduce
A
, can't convert the lambda tostd::function<A(T)>
.You obviously can explicit the correct
std::function
type callingmap()
and, taking in count that you're using C++17 (so you can use the deduction guides for
std::function
) also deducing the template parameters forstd::function
but, using again the template deduction guides for
std::function
, what about writingmat()
to accept a simply callable and deducingA
from it?I mean... what about something as follows?
(caution: code not tested).
You can also deduce
A
, withoutstd::function
deduction guides (so before C++17), as suggested by Michael Kenzel.