functional pointer to a templated function in c++ [duplicate] - c++

I'm trying to pass function std::max as template parameter to a templated function, but for some reasons compiler prints error that function type cannot be deduced. A simple example reproduces the same issue. It works with own max2 function but doesn't work with STL std::max:
#include <algorithm>
template <class T>
T max2(const T& a, const T& b) { return std::max(a, b); }
int main() {
#if 1
auto f = max2<float>;
#else
// error: unable to deduce ‘auto’ from ‘max<float>’
auto f = std::max<float>;
#endif
float max_val = f(1.0f, 2.0f);
return 0;
}

As seen here, std::max<float> isn't a single, unambiguous function. At this point, it's an overload set and there are still two possibilities:
constexpr const float& max( const float& a, const float& b );
constexpr float max( std::initializer_list<float> ilist );
You have two main options:
Wrap it in a lambda:
auto f = [](float a, float b) { return std::max(a, b); };
// Note there's no by-reference behaviour in this lambda.
If you want something more reusable, you'll need to wrap it separately, e.g., as something that doesn't require shenanigans to pass around:
struct max_fn {
template<typename T>
const T& operator()(const T& a, const T& b) const {
return std::max(a, b);
}
};
Obviously #2 comes with significant boilerplate, and that's ignoring other overloads and constexpr. In the future, it is expected that you will be able to do better. Today, you could emulate such a thing with a macro (most simply done by making the macro expand into a lambda). I've come across at least one LIFT macro that does this.
There's a third option that can be appealing because it's one line (an ugly line, but one line), and that's casting to the correct function pointer type. However, this isn't allowed except in a few special cases per [namespace.std]/6.

Related

What's the least verbose way to hint a overloaded function template as template parameter

I'm trying to create a function template which accepts std::max and others with compatible prototypes:
template <typename F>
int f(F g)
{
return g(1,2);
}
Because of the many overloads, the template argument F cannot be inferred f.e. for std::max, so this will fail:
f(std::max);
One possibility is to hint with a static_cast:
f(static_cast<int const& (*)(int const&,int const&)>(std::max));
I'm looking for a less verbose way while still being able to also easily pass any other sort of matching function, like f.e.
f([](int i, int j) { return 3; });
f(std::function<int(int,int)>([](int i, int j) { return 4; }));
Here's a godbolt.
If there's an easy way for C++11 and C++14 this would be great but if there's only one for C++17 and above this would also help a lot.
The issue is std::max not your function.
There is no way you could modify your function to make it accept a set of overloads. A set of overloads is just not something you can pass along. What you can pass along is an object with overloaded operator(). For example
struct foo {
template <typename T> bool operator()(T a,T b){return a+b;}
};
f(foo{}); // OK
std::max however is a function template and you need to instantiate the function before the function can be passed to f. Nothing you can do about that. Though you not necessarily need std::function or static_cast to pick the right overload. As mentioned in a comment you can do
f([](int a, int b) { return std::max(a, b); });
This triggers instantiation of std::max<int> inside the lambda, or if you prefer to select the type inside f you can do similar as in the above example:
f([](auto a, auto b) { return std::max(a,b); });
This lambda expression is similar to the above foo. You have a concrete object and only the operator() is a template which only gets instantiated when called, such that in f you could even do:
template <typename F>
int f(F g)
{
if (some_condition) return g(1,2); // calls std::max<int>
else return g(1.2,1.4); // calls std::max<double>
// (though int is returned anyhow)
}
f([](auto a, auto b) { return std::max(a,b); });

Using STL algorithms on unary functions

So I often want to perform some STL algorithm on a range of elements and instead of a comparison function, I would like to pass a unary function f.
For example I would like to write something like this
std::max_element(begin(range), end(range), f);
to find the maximum element in the range after applying f.
My current workaround looks something like that:
std::max_element(begin(range), end(range, [&f](auto a, auto b){ return f(a) < f(b); });
At first glance, this may look like no problem. But f could be a lambda expression itself or in another way more complicate than just f.
I have two problem with that piece of code:
a) It is error prone because one could accidently write f(a) < f(a) (especially if more complicated and one used copy and past). This is the problem of code duplication
b) It does not express the intent very well. If I want to sort by a function, I do not want to deal with a comparison.
Unfortunately I have not found a good solution to this kind of problem in the standard library (or even boost), so I would like to ask you what your solution to this problem is. Is there any reason for the non-existence of this overload in the algorithms?
Using c++ 20's ranges you can do:
std::ranges::max_element(range | std::views::transform(f));
One thing you can do is create your own generic comparator that accepts a unary function to perform a transform:
// Generic comparator
template<typename T, typename F>
struct transform_comparator_type
{
transform_comparator_type(F f): f(f) {}
bool operator()(T const& a, T const& b) const { return f(a) < f(b); }
F f;
};
// Helper function to deduce the type of F
template<typename T, typename F>
auto transform_comparator(F f)
{
return transform_comparator_type<T, F>(f);
}
int main()
{
std::vector<int> v{1, 4, 3, 6, 0};
auto e = std::max_element(std::begin(v), std::end(v),
transform_comparator<int>([v](int i){ return 2 * i; }));
// etc...
}
Edited to add:
The type can actually be deduced from the return type of the supplied transform function, so you don't need the helper function. You can do this instead:
template<typename F>
struct transform_comparator
{
using T = decltype(F()({})); // deduce T from return type of F
transform_comparator(F f): f(f) {}
bool operator()(T const& a, T const& b) const { return f(a) < f(b); }
F f;
};

How to pass std::sqrt as an argument

Im trying to create a generic function Foo that will accept an argument and Op what will be applied to it.
template <template<class> class Op>
float foo(float boo) {
return Op(boo);
}
template <template<class> class Op>
float foo(float a, float b) {
return Op(a, b);
}
void caller() {
float boo = 2.3;
auto res1 = foo<std::plus>(boo, boo); // works
auto res2 = foo<std::sqrt>(boo); // fail. error: no instance of overloaded function.
auto res3 = foo<std::exp>(boo); // fail. error: no instance of overloaded function
}
I think its related that std::sqrt is
"A set of overloads or a function template accepting an argument of
any integral type. "
while std::plus is
Function object for performing addition.
Can someone, please, help fix this? How do i pass std::sqrt and std::exp to foo?
As you've identified, the problem is that your template expects a type (because that's how you've written it), and though std::plus is a type (a functor), std::sqrt is a function.
It's hard to give a concrete solution for your problem, because you never showed your usage of Op.
But, generally, this is easy to do with an auto template parameter:
template <auto Op>
float foo(const float boo) {
return Op(boo);
}
If your version of C++ is too old, you'll need to add a version that takes a function pointer instead.
std::sqrt is an overloaded function, not a type. A simple fix would be to write a generic lambda that wraps std::sqrt, and then use its type when calling foo, like this:
auto sqrt = [](auto n) { return std::sqrt(n); };
auto res2 = foo<decltype(sqrt)>(boo); // ok
And you can do the same for std::exp.
Whether this is a good fix depends on how you want to use the Op parameter, which is not clear from the question.
You can't pass an overload set as a template argument. A simple workaround could be to wrap sqrt and exp into functors with a templated operator():
struct Sqrt {
template<class T>
T operator()(T t) const { return std::sqrt(t); }
};
struct Exp {
template<class T>
T operator()(T t) const { return std::exp(t); }
};
Then the following will work
auto res2 = foo<Sqrt>(boo);
auto res3 = foo<Exp>(boo);

Why duplicate code is needed with const reference arguments?

In this interview Stepanov shows how to implement generic max function in C++.
Try to implement a simple thing in the object oriented way, say, max.
I do not know how it can be done. Using generic programming I can
write:
template <class StrictWeakOrdered>
inline StrictWeakOrdered& max(StrictWeakOrdered& x,
StrictWeakOrdered& y) {
return x < y ? y : x;
}
and
template <class StrictWeakOrdered>
inline const StrictWeakOrdered& max(const StrictWeakOrdered& x,
const StrictWeakOrdered& y) {
return x < y ? y : x;
}
(you do need both & and const &).
Why is there need to write the code twice? Is this needed to aid compiler for optimization or a convention to reduce bugs? Is max a special case where body of a const version is identical?
How many valid const and non-const permutations a function of N arguments should have to define a complete API?
First of all, you need the non-const version to allow stuff like
max(a, b) = something;
If you don't want to do such things, you can just provide the const version only to cover all cases. That is basically what the standard std::max does.
You also do not need to provide any more permutations of const and non-const, returning non-const& only makes sense if all inputs are non-const, all other cases are properly handled by the const version.
If you want to avoid code duplication, you can do something like this:
template <class StrictWeakOrdered>
inline StrictWeakOrdered& max(StrictWeakOrdered& x, StrictWeakOrdered& y) {
const auto &xr = x;
const auto &yr = y;
return const_cast<StrictWeakOrdered&>(max(xr, yr));
}
In this special case, the const_cast is safe because you already know that the input is really non-const. Now you only have to provide the implementation for the const case.
So providing the implementation twice is not required and should not help the compiler, but whether or not the above is more readable than what Stepanov did is debatable.
You actually don't need both versions. You can write it this way.
template <class S, class T>
decltype(auto) max(S&& a, T&& b) {
using Ret = std::conditional_t<
std::is_const<std::remove_reference_t<S>>::value, S, T>;
if (b < a)
return std::forward<Ret>(a);
return std::forward<Ret>(b);
}
Falling back to const if either of the arguments was const.
If you do not intend to modify the argument, you can just go with the const& version. Everything should bind to a const reference.
C++11 also introduced reference collapsing, and a template parameter T&& is sometimes called a universal reference. In this case, when instantiating the parameter type for e.g. a int&, we would have int& && which collapses to int&. Now, you can write the function as
template <class T1, class T2>
inline T1 const& max(T1&& x, T2&& y) {
T1 const& x_=x;
T2 const& y_=y;
return (x_ < y_) ? (y_) : (x_);
}
This can be called with const values, temporaries (r-values) and mutable variables:
int const a=1;
int b=2;
max(b,b) = 23;
std::cout << max(a,a) << max( int{4}, int{5} ) << b << max(int{4}, a);

How can I pass a lambda (c++11) into a templated function?

I'm playing around with lambda functions in gcc 4.6.2, and would like to implement a templated "map" function like this:
template<typename A, typename B> std::vector<B> map(const std::vector<A>& orig, const std::function<B(A)> f) {
std::vector<B> rv;
rv.resize(orig.size());
std::transform(begin(orig), end(orig), begin(rv), f);
return rv;
}
This doesn't work, because the test code:
int main(int argc, char **argv) {
std::vector<int> list;
list.push_back(10);
list.push_back(20);
list.push_back(50);
std::vector<int> transformed = map(list, [](int x) -> int { return x + 1; });
std::for_each(begin(transformed), end(transformed), [](int x) { printf("-> %d\n", x); });
return 0;
}
gives this error:
test.cpp:49:80: error: no matching function for call to ‘map(std::vector<int>&, main(int, char**)::<lambda(int)>)’
test.cpp:49:80: note: candidate is:
test.cpp:6:49: note: template<class A, class B> std::vector<B> map(const std::vector<A>&, std::function<B(A)>)
If I remove the templating, and use a vector directly, it compiles fine:
std::vector<int> map(const std::vector<int>& orig, const std::function<int(int)> f) {
std::vector<int> rv;
rv.resize(orig.size());
std::transform(begin(orig), end(orig), begin(rv), f);
return rv;
}
so it must be a problem with the way I'm defining the template.
Has anyone run into this before? I know lambdas are incredibly new.
You don't need to use std::function. Just make the predicate parameter a template value. For example,
template<typename A, typename B> std::vector<B> map(const std::vector<A>& orig, B f) {
std::function<> is more useful as a member value type or for defining non-templated code.
The problem is that the compiler can't figure out what to use for B. In order to determine that type it wants to use the function<> you pass in for f, but you don't pass an std::function<> directly. You pass in something you expect to be used to construct a function<>. And in order to do that implicit construction it needs to know the type of argument. So you've got this circular dependency where the type of argument depends on what you pass in, but what gets passed in depends on the type of argument.
You can break this circular dependency by specifying the template parameters, such as map_<int,int>(list, [](int x) -> char { return x + 1; });
(although I see the functor actually returns a char, not an int, so if the type deduction worked for you here you'd be getting back a vector<char> which cannot be converted to a vector<int> when you assign the result to transformed)
However as has been pointed out, generally templates take functors as just a plain template type:
template<typename A,typename Func>
auto map_(const std::vector<A>& orig, Func f) -> std::vector<decltype(f(A()))> {
std::vector<decltype(f(A()))> rv;
/*...*/
}
(we use the trailing return type because we need to use the expression f in the return type, which isn't available unless the return type comes afterwards.)
This allows the template to deduce the functor type directly and avoids any type conversions and best allows for optimization.
It's also customary to use iterators as arguments on these sorts of functions, in which case your function is just a wrapper around std::transform, so you can just use that directly. I'm not sure there's a whole lot of value in a special version that deals with vectors specifically.
I'm tackling with lambdas too and i noticed that you can declare a function pointer in a function definition's parameter list and when you make a call to that function you can pass a lambda expression as an argument if it matches the function prototype of course.
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
template <typename T,typename C>
struct map {
typedef C (*F)(const T&);
std::vector<C> rv;
map () {}
map (const std::vector<T>& o,F f) {
rv.resize(o.size());
std::transform (o.begin(),o.end(),rv.begin(),f);
}
~map () {}
operator std::vector<C> () const {
return rv;
}
};
int main () {
std::vector<int> asd(5,12);
std::vector<char> transformed=map<int,char>(asd,[](const int& x)->char {return x+1;});
std::copy (transformed.begin(),transformed.end(),std::ostream_iterator<int>(std::cout," "));
}