I'm playing around with some C++11 features, and I encountered the following:
#include <iostream>
#include <vector>
template <class T>
void map(std::vector<T>& values, T(*func)(T)) {
for (int &value : values) {
value = func(value);
}
}
int mul2(int x) {
return 2*x;
}
auto mul3 = [](int value) {
return value * 3;
};
int main() {
std::vector<int> v = { 1,2,3,4,5 };
map(v, mul3);
for (auto value : v) {
std::cout << value << std::endl;
}
}
using map with mul2 works as expected, but when I use the mul3 function it gives a compilation error. I expected that auto in this case would give me a int function pointer, but it seems that is not the case here. Anybody could explain this behaviour?
The lambda can implicitly be converted to a function pointer, but that's not what's failing here. Rather, the compiler is failing to deduce T because the lambda-to-function-pointer conversion doesn't happen during deduction.
main.cpp:5:6: note: template argument deduction/substitution failed:
main.cpp:21:16: note: mismatched types 'T (*)(T)' and '<lambda(int)>'
21 | map(v, mul3);
| ^
The compiler can make the connection between T(*)(T) and int(*)(int) and it can make the connection between int(*)(int) and the lambda type, but it can't make the connection between T(*)(T) and the lambda type.
You can fix this by making one of the two connections for it: explicitly specifying the function's template argument, or casting the lambda to the function pointer type. The first skips the deduction step an then the implicit lambda-to-function-pointer conversion succeeds. The second allows deduction to succeed because the second parameter is a compatible function pointer type.
// Option 1: Specifying T allows implicit conversion of the lambda to fnptr
map<int>(v, mul3);
// Option 2a: Cast of lambda to fnptr allows T to be deduced
map(v, static_cast<int(*)(int)>(mul3));
// Option 2b: Converting lambda to fnptr at mul3 initialization allows T to be deduced
int (*mul3)(int) = [](int value) {
return value * 3;
};
However, I would recommend fixing the issue a different way -- there's no reason the function has to work with vectors, function pointers, and ints. Why can't it work with linked lists, functors, and doubles? There's really no reason to constrain the types like this; just let them be whatever they are and see if the instantiation succeeds:
template <class TContainer, TFunction>
void map(TContainer & values, TFunction const & func) {
for (auto &value : values) {
value = func(value);
}
}
Expanded from my comment (when the question was closed), you can template away the function details using the functor template "pattern":
template <class T, typename Functor>
void map(std::vector<T>& values, Functor func) {
for (int &value : values) {
value = func(value);
}
}
See here for full example: https://godbolt.org/z/fdHvAP
Related
I have a struct with a method called call which has a const overload. The one and only argument is a std::function which either takes a int reference or a const int reference, depending on the overload.
The genericCall method does exactly the same thing but uses a template parameter instead of a std::function as type.
struct SomeStruct {
int someMember = 666;
void call(std::function<void(int&)> f) & {
f(someMember);
std::cout << "call: non const\n";
}
void call(std::function<void(const int&)> f) const& {
f(someMember);
std::cout << "call: const\n";
}
template <typename Functor>
void genericCall(Functor f) & {
f(someMember);
std::cout << "genericCall: non const\n";
}
template <typename Functor>
void genericCall(Functor f) const& {
f(someMember);
std::cout << "genericCall: const\n";
}
};
When I now create this struct and call call with a lambda and auto & as argument the std::function always deduces a const int & despite the object not being const.
The genericCall on the other hand deduces the argument correctly as int & inside the lamdba.
SomeStruct some;
some.call([](auto& i) {
i++; // ?? why does auto deduce it as const int & ??
});
some.genericCall([](auto& i) {
i++; // auto deduces it correctly as int &
});
I have no the slightest clue why auto behaves in those two cases differently or why std::function seems to prefer to make the argument const here. This causes a compile error despite the correct method is called. When I change the argument from auto & to int & everything works fine again.
some.call([](int& i) {
i++;
});
When I do the same call with a const version of the struct everything is deduced as expected. Both call and genericCall deduce a const int & here.
const SomeStruct constSome;
constSome.call([](auto& i) {
// auto deduces correctly const int & and therefore it should
// not compile
i++;
});
constSome.genericCall([](auto& i) {
// auto deduces correctly const int & and therefore it should
// not compile
i++;
});
If someone could shine some light on this I would be very grateful!
For the more curious ones who want to dive even deeper, this problem arose in the pull request: https://github.com/eclipse-iceoryx/iceoryx/pull/1324 while implementing a functional interface for an expected implementation.
The issue is that it's a hard error to try to determine whether your lambda is Callable with const int & returning void, which is needed to determine whether you can construct a std::function<void(const int&)>.
You need to instantiate the body of the lambda to determine the return type. That's not in the immediate context of substituting a template argument, so it's not SFINAE.
Here's an equivalent error instantiating a trait.
As #aschepler notes in the comments, specifying a return type removes the need to instantiate the body of your lambda.
The problem is that generic lambdas (auto param) are equivalent to a callable object whose operator() is templated. This means that the actual type of the lambda argument is not contained in the lambda, and only deduced when the lambda is invoked.
However in your case, by having specific std::function arguments, you force a conversion to a concrete type before the lambda is invoked, so there is no way to deduce the auto type from anything. There is no SFINAE in a non-template context.
With no specific argument type, both your call are valid overloads. Actually any std::function that can match an [](auto&) is valid. Now the only rule is probably that the most cv-qualified overload wins. You can try with a volatile float& and you will see it will still choose that. Once it choose this overload, the compilation will fail when trying to invoke.
I have this function:
template<typename T, int (*F)(T&)>
void DoStuff(T& s)
{
auto an = make_any<T>(s);
cout << _GetDataFromAny<MyStruct, F>(an);
}
Which needs to be called like this:
DoStuff<MyStruct, Fun>(s);
This works fine, however I don't like having to specify the first type, since it is part of the signature of the second parameter. I would like to be able to deduce it such that I can just call:
DoStuff<Fun>(s);
However, I don't know how to specify in the template that the type T needs to be deduced from the signature of the function F.
Is this possible?
You can write a helper that deduces the argument type of a function pointer that returns int:
template<typename T>
T arg_type(int(*)(T&));
and then rewrite your function template slightly to take in a function pointer as a non-type template parameter, and figure out the argument type from that
template<auto F, typename T = decltype(arg_type(F))>
void DoStuff(T& s) {
// ...
}
Disclaimer: This answer went through a series of edits and corrections, many thanks goes to Jarod42 for his patience and help, and cigien for fruitful discussion. It is a bit longer than necessary, but I felt it is worth to keep a bit of the history. It is: the most simple / the one I would prefer / some explanation of the previous confusion. For a quick answer, read the second part.
The simple
You can use an auto template parameter (since C++17) for the function pointer and let T be deduced from the argument:
template<auto F, typename T>
void DoStuff(T& s)
{
int x = F(s);
}
int foo(double&){ return 42;}
int main() {
double x;
DoStuff<&foo>(x);
}
Live Demo
The "right"
The downside of the above is that F and T are "independent". You can call DoStuff<&foo>(y) with eg a std::string y; and instantiation will only fail when calling F. This might lead to a unnecessarily complex error message, depending on what you actually do with F and s. To trigger the error already at the call site when a wrong T is passed to a DoStuff<F> you can use a trait to deduce the argument type of F and directly use that as argument type of DoStuff:
template <typename T> struct param_type;
template <typename R,typename P>
struct param_type< R(*)(P&)> {
using type = P;
};
template<auto F>
void DoStuff(typename param_type<decltype(F)>::type& s)
{
int x = F(s); // (1)
}
int foo(double&){ return 42;}
int main() {
double x;
DoStuff<foo>(x);
std::string y;
DoStuff<foo>(y); // (2) error
}
Now the error that before would only happen inside the template (1) happens already in main (2) and the error message is much cleaner.
Live Demo
The "wrong"
Mainly as curiosity, consider this way of deducing the parameter type from the function pointer:
template <typename T> struct param_type;
template <typename R,typename P>
struct param_type< R(*)(P&)> {
using type = P;
};
template<auto F, typename T = typename param_type<decltype(F)>::type>
void DoStuffX(T& s)
{
}
int foo(double&){ return 42;}
int main() {
double x;
DoStuffX<foo>(x);
}
This was my original answer, but it was not actually doing what I thought it was doing. Note that I was not actually calling F in DoStuff and to my surprise this compiled:
int main() {
std::string x;
DoStuffX<foo>(x);
}
The reason is that the default template argument is not used when T can be decuded from the passed parameter (see here). That is, DoStuffX<foo>(x); actually instantiates DoStuffX<foo,std::string>. We can still get our hands on the default via:
int main() {
std::string x;
auto f_ptr = &DoStuffX<foo>;
f_ptr(x); // error
}
Now calling DoStuffX<foo> with a std::string is a compiler error, because here DoStuffX<foo> is instantiated as DoStuffX<foo,double>, the default argument is used (there is no parameter that could be used to deduce T when DoStuffX is instantiated).
I'm trying to use a function pointer with template as an argument. But the compiler seems to have trouble handling lambda and nullptr.
Everything is fine when I change void (*callback)(T input) to void (*callback)(int input) in the following code.
Is this compiler behavior specified by the C++ standard?
The compile command I use is $ g++ main.cpp -std=c+11 but the same behavior found in Visual Studio 2019.
template <class T>
int dummy (T tmp, void (*callback)(T input)) {
// Doesn't do anything, just trying to compile
// If I change (T input) to (int input), it compiles fine
if (callback)
return 1;
else
return 0;
}
void callback (int input) {
return;
}
int main () {
int tmp = 10;
auto callback_lambda = [](int input) -> void {
return;
};
dummy(tmp, callback); // Compiles OK
dummy(tmp, callback_lambda); // Error: mismatched types 'void (*)(T)' and 'main()::<lambda(<int>)'
dummy(tmp, nullptr); // Error: no matching function for call to 'dummy(int&, std:nullptr_t)'
return 0;
}
The problem is that implicit conversion won't be considered in template argument deduction.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
So when passing lambda and nullptr, the conversions to the function pointer aren't considered, the template parameter T can't be deduced for the 2nd function argument and then cause the error.
You can make the 2nd function parameter to non-deduced context, to exclude it from deduction, with the help of std::type_identity.
type_identity can be used to block template argument deduction:
e.g.
template <class T>
int dummy (T tmp, void (*callback)(std::type_identity_t<T>)) {
...
}
LIVE
PS: If your compiler doesn't support std::type_identity (since C++20), you can define your own version, it's not difficult.
This question already has an answer here:
Constructing std::function argument from lambda
(1 answer)
Closed 3 years ago.
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!
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 deduce A.
And if the compiler can't deduce A, can't convert the lambda to std::function<A(T)>.
You obviously can explicit the correct std::function type calling map()
a.map(std::function<std::uint32_t(std::uint32_t)>{[]( uint32_t c ) -> uint32_t {
return c * c;
}});
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 for std::function
a.map(std::function{[]( uint32_t c ) -> uint32_t {
return c * c;
}});
but, using again the template deduction guides for std::function, what about writing mat() to accept a simply callable and deducing A from it?
I mean... what about something as follows?
template <typename F>
auto map( F && func ) const {
using A = typename decltype(std::function{std::forward<F>(func)})::result_type;
auto sequence = new Sequence< A >;
for ( const T& element : *this ) {
sequence->push( std::forward<F>(func)( element ) );
}
return *sequence;
}
(caution: code not tested).
You can also deduce A, without std::function deduction guides (so before C++17), as suggested by Michael Kenzel.
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 an std::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.:
template <typename F>
auto map(F&& f) const;
You should be able to deduce the result type of whatever invoking f on an element of your sequence ends up producing, e.g., via
decltype(f(std::declval<T>()))
Furthermore, don't just return a raw pointer to a new Sequence. Use an std::unique_ptr.
Putting it all together, your map function could look something like this:
template <typename F>
auto map(F&& f) const
{
using output_element_type = decltype(f(std::declval<T>()));
auto sequence = std::make_unique<Sequence<output_element_type>>();
for (const T& element : *this)
sequence->push(f(element));
return sequence;
}
I was thinking about the implicit templates of C++14, and I'm trying to declare a function to match an specific argument type (SFINAE and traits still give me headaches). I'm not sure how to explain what I want, but I'm trying to make a Y combinator (just to see if it's possible, not intended for production).
I'm trying to declare a function:
template<typename T>
my_traits<T>::return_type Y(T t) {
// ...
};
Such that T is a function (or a functor) that matches
std::function<R(F, Args...)>
// where F (and above return_type) will be
std::function<R(Args...)>
Which would take any number of arguments, but the first should be a function with the same return type and the same arguments (except this function itself). The first parameter to the operator () of the functor is a template.
The usage I want to achieve:
auto fib = [](auto myself, int x) {
if(x < 2)
return 1;
return myself(x - 1) + myself(x - 2);
};
// The returned type of fib should be assignable to std::function<int(int)>
I wasn't able to take the return type of the T type (because of the overloaded operator ()). What I'm trying to make is possible? How could I make it?
Edit:
Seeing it from a different angle, I'm trying to make this work:
struct my_functor {
template<typename T>
char operator () (T t, int x, float y) { /* ... */ };
};
template<typename T>
struct my_traits {
typedef /* ... */ result_type;
/* ... */
};
// I want this to be std::function<char(int, float)>, based on my_functor
using my_result =
my_traits<my_functor>::result_type;
It is not possible in C++14 return type deduction to deduce int(int) out of int(T, int) as OP desires.
However, we can mask the first parameter of the result using the following approach. The struct YCombinator is instantiated with a non-recursive function object member, whose first argument is a version of itself without the first argument. YCombinator provides a call operator that receives the arguments of the non-recursive function and then returns its function object member after substituting itself for the first argument. This technique allows the programmer to avoid the messiness of myself(myself, ...) calls within the definition of the recursive function.
template<typename Functor>
struct YCombinator
{
Functor functor;
template<typename... Args>
decltype(auto) operator()(Args&&... args)
{
return functor(*this, std::forward<Args>(args)...);
}
};
A make_YCombinator utility template allows for a streamlined usage pattern. This compiles run runs in GCC 4.9.0.
template<typename Functor>
decltype(auto) make_YCombinator(Functor f) { return YCombinator<Functor> { f }; }
int main()
{
auto fib = make_YCombinator([](auto self, int n) -> int { return n < 2 ? 1 : self(n - 1) + self(n - 2); });
for (int i = 0; i < 10 ; ++i)
cout << "fib(" << i << ") = " << fib(i) << endl;
return 0;
}
Since the non-recursive function is not defined at time that the recursive function is defined, in general the recursive function must have an explicit return type.
Edit:
However, it may be possible for the compiler to deduce the return type in certain cases if the programmer takes care to indicate the return type of the recursive function before use of the non-recursive function. While the above construction requires an explicit return type, in the following GCC 4.9.0 has no problem deducing the return type:
auto fib = make_YCombinator([](auto self, int n) { if (n < 2) return 1; return self(n - 1) + self(n - 2); });
To pin this down just a bit further, here is a quote from the draft C++14 standard on return type deduction [7.1.6.4.11]:
If the type of an entity with an undeduced placeholder type is needed
to determine the type of an expression, the program is ill-formed.
Once a return statement has been seen in a function, however, the
return type deduced from that statement can be used in the rest of the
function, including in other return statements. [ Example:
auto n = n; // error, n’s type is unknown
auto f();
void g() { &f; } // error, f’s return type is unknown
auto sum(int i) {
if (i == 1)
return i; // sum’s return type is int
else
return sum(i-1)+i; // OK, sum’s return type has been deduced
}
—end example ]
It's a really hacky approach, and has severe limitations, but here it goes:
First, we need a class that pretends to support every possible operation (as far as possible), such as the fake_anything class. Note that this isn't perfect since at a minimum . and :: won't work. To fake a functor, we give it a function call operator:
template<class... Ts> fake_anything operator()(Ts&&...) const;
Knowing that the lambda has only one operator(), and that operator() has only one template parameter allows us to extract its signature with decltype(&T::operator()<fake_anything>).
For this to work, the lambda's return type must be explicitly specified; it can't use deduction, since otherwise the deduced return types will probably conflict.
Finally we can obtain the other arguments to the lambda and the return type using the standard partial specialization approach:
template<class T>
struct extract_signature;
template<class T, class R, class FA, class...Args>
struct extract_signature<R (T::*)(FA, Args...)> {
static_assert(std::is_same<fake_anything, std::decay_t<FA>>::value, "Unexpected signature");
using type = std::function<R(Args...)>;
};
template<class T, class R, class FA, class...Args>
struct extract_signature<R (T::*)(FA, Args...) const> {
static_assert(std::is_same<fake_anything, std::decay_t<FA>>::value, "Unexpected signature");
using type = std::function<R(Args...)>;
};
// other cv- and ref-qualifier versions omitted - not relevant to lambdas
// we can also static_assert that none of Args is fake_anything, or reference to it, etc.
And add an alias template to hide all the ugliness of the hack:
template<class T>
using signature_t = typename extract_signature<decltype(&T::template operator()<fake_anything>)>::type;
And finally we can check that
static_assert(std::is_same<signature_t<decltype(fib)>,
std::function<int(int)>>::value, "Oops");
Demo.
The limitations:
The return type of operator() must be explicitly specified. You cannot use automatic return type deduction, unless all of the return statements return the same type regardless of the return type of the functor.
The faking is very imperfect.
This works for operator() of a particular form only: template<class T> R operator()(T, argument-types...) with or without const, where the first parameter is T or a reference to possibly cv-qualified T.