templated function argument in C++14 - c++

This code does not compile, not even under C++14, because of problems with template type deduction. What is the least inelegant workaround?
#include <vector>
#include <functional>
#include <iostream>
template <class T>
std::vector<T> merge_sorted(
const std::vector<T>& a, const std::vector<T>& b,
std::function<bool(const T, const T)> a_before_b)
{
std::vector<T> ret;
auto ia=a.begin();
auto ib=b.begin();
for (;;ia!=a.end() || ib!=b.end())
ret.push_back( a_before_b(*ia,*ib) ? *(ia++) : *(ib++) );
return ret;
}
int main()
{
std::vector<double> A { 1.1, 1.3, 1.8 };
std::vector<double> B { 2.1, 2.2, 2.4, 2.7 };
auto f = [](const double a, const double b) -> bool {
return (a-(long)(a))<=(b-(long(b))); };
std::vector<double> C = merge_sorted(A, B, f);
for (double c: C)
std::cout << c << std::endl;
// expected outout: 1.1 2.1 2.2 1.3 2.4 2.7 1.8
}
Here the error message from g++ -std=c++14 main.cpp:
main.cpp: In function ‘int main()’:
main.cpp:23:49: error: no matching function for call to ‘merge_sorted(std::vector<double>&, std::vector<double>&, main()::<lambda(double, double)>&)’
std::vector<double> C = merge_sorted(A, B, f);
^
main.cpp:6:16: note: candidate: template<class T> std::vector<T> merge_sorted(const std::vector<T>&, const std::vector<T>&, std::function<bool(T, T)>)
std::vector<T> merge_sorted(
^~~~~~~~~~~~
main.cpp:6:16: note: template argument deduction/substitution failed:
main.cpp:23:49: note: ‘main()::<lambda(double, double)>’ is not derived from ‘std::function<bool(T, T)>’
std::vector<double> C = merge_sorted(A, B, f);
==
Later edit, just for the record: Here comes a version of the code that compiles (thanks to received answers) and that executes correctly (several corrections of the above untested code):
#include <vector>
#include <functional>
#include <iostream>
template <class T, class Pred>
std::vector<T> merge_sorted(const std::vector<T>& a, const std::vector<T>& b, Pred a_before_b)
{
std::vector<T> ret;
auto ia=a.begin();
auto ib=b.begin();
for (;ia!=a.end() && ib!=b.end();)
ret.push_back( a_before_b(*ia,*ib) ? *(ia++) : *(ib++) );
for (;ia!=a.end();)
ret.push_back( *(ia++) );
for (;ib!=b.end();)
ret.push_back( *(ib++) );
return ret;
}
int main()
{
std::vector<double> A { 1.1, 1.3, 1.8 };
std::vector<double> B { 2.1, 2.2, 2.4, 2.7 };
auto f = [](const double a, const double b) -> bool {
return (a-(long)(a))<=(b-(long(b))); };
std::vector<double> C = merge_sorted(A, B, f);
for (double c: C)
std::cout << c << std::endl;
// expected outout: 1.1 2.1 2.2 1.3 2.4 2.7 1.8
}

The problem here is that f is not a std::function. It is some unnamed class type but it is not a std::function. When the compiler does template argument deduction it does not do any conversions, it works with the parameters as is to deduce their type. That means where it expects to see a std::function<bool(const T, const T)> it sees main()::<lambda(double, double)> as that is the type of the lambda and since those types do not match the deduction fails. In order to get deduction to succeed you need to get them to match.
Without changing the function signature you have to cast f to a std::function in order to get it to work. That would look like
std::vector<double> C = merge_sorted(A, B, static_cast<std::function<bool(const double,const double)>>(f));
If you do not mind changing the function signature then we can use
template <class T, class Func>
std::vector<T> merge_sorted(
const std::vector<T>& a, const std::vector<T>& b,
Func a_before_b)
And now it doesn't matter if you pass a std::function or a lambda or a functor.

You need to make the type of a_brefore_b a non-deduced context somehow. I generally introduce a suitably-named helper for this:
template <class T>
struct NonDeduced
{
using type = T;
};
template <class T>
std::vector<T> merge_sorted(
const std::vector<T>& a, const std::vector<T>& b,
typename NonDeduced<std::function<bool(const T, const T)>>>::type a_before_b)
Of course (as #Marc Glisse pointed out in comments), it's quite unnecessary to force use of std::function for the type of a_before_b in the first place. Not to mention the fact that it can easily come with a performance penalty (std::function uses type erasure and dynamic dispatch internally). Just follow what the Standard Library does and type the predicate by a template parameter:
template <class T, class Pred>
std::vector<T> merge_sorted(
const std::vector<T>& a, const std::vector<T>& b,
Pred a_before_b)

The errror comes from the compiler trying to deduce T where it can't deduce T for the std::function parameter which gets passed a lambda.
The standard uses plain template paramters for such predicates for good reasons.
2.1 The predicate is most generic using a template paramter.
You can pass in std::function, std::bind, function pointers, lambdas, functors...
2.2 Inlining (if possible) is most likely to occur.
With a whole bunch of luck a compiler is smart enough to inline a lambda despite being passed "through" a std::function into a template but I wouldn't bet on that. On the contrary, I'd actually expect a compiler to inline a lambda (if suitable) if I pass it via its own type.
Your code has several other issues.
3.1 for (;;ia!=a.end() || ib!=b.end()) here ; is set incorrectly.
3.2 Even with correctly set ; the predicate is wrong since ia!=a.end() || ib!=b.end() will keep the loop running eventhough either ia == a.end() or ib == b.end() is true. Within the loop both iterators are dereferenced to check the predicate which leads us into undefined behaviourland if we are already one past the last element. The loop condition must therefore be for (;ia!=a.end() && ib!=b.end();) which leaves us with elements in either a or b.
Here is what you would probably want to write if you're after performance and generality:
template <class InIt, class OutIt, class Predicate>
auto merge_sorted(InIt first1, InIt last1, InIt first2, InIt last2,
OutIt dest, Predicate pred)
{
// as long as we have elements left in BOTH ranges
for (;first1 != last1 && first2 != last2; ++dest)
{
// check predicate which range offers the lowest value
// and insert it
if (pred(*first1, *first2)) *dest = *(first1++);
else *dest = *(first2++);
}
// here either first1 == last1 or first2 == last2 is true
// thus we can savely copy the "rest" of both ranges
// to dest since we only have elements in one of them left anyway
std::copy(first1, last1, dest);
std::copy(first2, last2, dest);
return pred;
}

Since I can not comment: Generally what #NathanOliver said. A lambda expression can not be "cast" to a std::function, since it is - internally - a different kind of construct.
Of course it would be nice if the compiler could infer (via static analysis) it has to create a std::function object for the lambda. But that doesn't seem to be part of C++11/C++14.
To resolve this, I find it easiest to add a typename to the template:
template <class T, typename F>
std::vector<T> merge_sorted(
const std::vector<T>& a, const std::vector<T>& b,
F& a_before_b)
Of course you can also use class. See question Use 'class' or 'typename' for template parameters? and the old MSDN article here.
Also, note you have a typo in line 13. You probably meant:
for (;;ia!=a.end() || ib!=b.end())

Related

Problem using std::transform with lambdas VS std::transform with std::bind

I am using Clang 14.0.0 and C++20. My goal is to write a general function in order to use std::lerp on a set of elements. So I came up with this:
template <typename InputIterator, typename OutputIterator>
constexpr OutputIterator
lerp_element(InputIterator first, InputIterator last, OutputIterator result,
std::iter_value_t<InputIterator> const& a,
std::iter_value_t<InputIterator> const& b)
{
return std::transform(first, last, result, [&a, &b] (auto const &t) { return std::lerp(a, b, t); });
}
This works well, however - in an attempt to find a better/alternative way - I thought that maybe a version with std::bind could be more concise and elegant:
template <typename InputIterator, typename OutputIterator>
constexpr
OutputIterator
lerp_element(InputIterator first, InputIterator last, OutputIterator result,
std::iter_value_t<InputIterator> const& a,
std::iter_value_t<InputIterator> const& b)
{
return std::transform(first, last, result, std::bind(std::lerp(a, b, std::placeholders::_3)));
}
Lastly I found that std::bind_front allows me to be even less verbose:
template <typename InputIterator, typename OutputIterator>
constexpr
OutputIterator
lerp_element(InputIterator first, InputIterator last, OutputIterator result,
std::iter_value_t<InputIterator> const& a,
std::iter_value_t<InputIterator> const& b)
{
return std::transform(first, last, result, std::bind_front(std::lerp(a, b)));
}
The problem is when I try to compile the two latter versions (the ones that use std::bind and std::bind_front), Clang throws this error message at me:
Here is the calling code:
int main() {
std::vector<double> v = { 0.11, 0.53, 0.32, 0.29, 0.77, 0.45, 0.96, 0.0, 1.0 };
std::vector<double> results;
lerp_element(std::begin(v), std::end(v), std::back_inserter(results), 10.0, 20.0);
for (auto x : results) { std::cout << x << ' '; }
return 0;
}
Godbolt
Since I have the version with the lambda working, it seems the issue must be in the way I am using std::bind. I have been reading everything I could find online on how to properly use std::bind and std::placeholders, but obviously I must not be getting it.
I understand the problem has to do with std::lerp not receiving consistent argument types in order to resolve the overload, so:
why does that happen?
how do I modify the std::bind/std::bind_front versions to make it compile and work as expected?
The function argument to std::bind or std::bind_front should not be invoked, but rather the function itself is passed as the first argument:
using T = std::iter_value_t<InputIterator>;
using F = T (*)(T, T, T) noexcept;
return std::transform(first, last, result,
std::bind_front<F>(std::lerp, a, b));
Godbolt

Can type arguments be made deduceable for function templates using std container?

I found this implementation of a few common features of functional programming, e.g. map / reduce:
(I'm aware stuff like that is aparently coming or partially present in new C++ versions)
github link
A part of the code:
template <typename T, typename U>
U foldLeft(const std::vector<T>& data,
const U& initialValue,
const std::function<U(U,T)>& foldFn) {
typedef typename std::vector<T>::const_iterator Iterator;
U accumulator = initialValue;
Iterator end = data.cend();
for (Iterator it = data.cbegin(); it != end; ++it) {
accumulator = foldFn(accumulator, *it);
}
return accumulator;
}
template <typename T, typename U>
std::vector<U> map(const std::vector<T>& data, const std::function<U(T)> mapper) {
std::vector<U> result;
foldLeft<T, std::vector<U>&>(data, result, [mapper] (std::vector<U>& res, T value) -> std::vector<U>& {
res.push_back(mapper(value));
return res;
});
return result;
}
Usage example:
std::vector<int> biggerInts = map<int,int>(test, [] (int num) { return num + 10; });
The type arguments T,U have to be fully qualified for this to compile, as shown in the example, with e.g. map< int,int >( ... ).
This implementation is for C++11, as mentioned on the linked-to page.
Is it possible with newer C++ versions (or even 11) now to make the use of this less verbose, i.e. making the types U,T deduce automatically?
I have googled for that and only found that there is apparently some improvement for class template, as opposed to function template, argument deduction in C++17.
But since I only ever used templates in a rather basic manner, I was wondering whether there is something in existence that I'm not aware of which could improve this implementation verboseness-wise.
You can rewrite map signature to be:
template <typename T, typename M, typename U = decltype(std::declval<M>()(T{}))>
std::vector<U> map(const std::vector<T>& data, const M mapper)
then T will be deduced as value_type of vector's items.
M is any callable object.
U is deduced as return type of M() functor when called for T{}.
Below
std::vector<int> biggerInts = map(test, [] (int num) { return num + 10; });
^^^^ empty template arguments list
works fine.
Live demo
More general templates make template argument deduction easier.
One principle: it is often a mistake to use a std::function as a templated function's parameter. std::function is a type erasure, for use when something needs to store some unknown invokable thing as a specific type. But templates already have the ability to handle any arbitrary invokable type. So if we just use a generic typename FuncT template parameter, it can be deduced for a raw pointer-to-function, a lambda, or another class with operator() directly.
We might as well also get more general and accept any input container instead of just vector, then determine T from it, if it's even directly needed.
So for C++11 I would rewrite these:
// C++20 is adding std::remove_cvref, but it's trivial to implement:
template <typename T>
using remove_cvref_t =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
template <typename Container, typename U, typename FuncT>
remove_cvref_t<U> foldLeft(
const Container& data,
U&& initialValue,
const FuncT& foldFn) {
remove_cvref_t<U> accumulator = std::forward<U>(initialValue);
for (const auto& elem : data) {
accumulator = foldFn(std::move(accumulator), elem);
}
return accumulator;
}
template <typename Container, typename FuncT>
auto map(const Container& data, const FuncT& mapper)
-> std::vector<remove_cvref_t<decltype(mapper(*std::begin(data)))>>
{
using T = remove_cvref_t<decltype(*std::begin(data))>;
using ResultT = std::vector<remove_cvref_t<decltype(mapper(std::declval<const T&>()))>>;
ResultT result;
foldLeft(data, std::ref(result), [&mapper] (ResultT &res, const T& value) -> ResultT& {
res.push_back(mapper(value));
return res;
});
return result;
}
See the working program on coliru.
There was one unfortunate thing about the old map: it potentially copied the result vector at every iteration. The = in accumulator = foldFn(accumulator, *it); is a self-assignment, which might do nothing, or might allocate new memory, copy contents, then free the old memory and update the container. So instead I've changed the U for foldLeft in this case to a std::reference_wrapper. The = in that case will still "rebind" the wrapper to the same object, but that will at least be quick.
In C++14 and later, you could do away with finding T within map by using a generic lambda: [&mapper] (std::vector<U>& res, const auto& value) ...

C++ different minmax implementation

As You may (not) know using std::minmax with auto and temporary arguments may be dangerous. Following code for example is UB because std::minmax returns pair of references, not values:
auto fun(){
auto res = std::minmax(3, 4);
return res.first;
}
I would like to ask if there is possibility to make std::minmax function act safely or at least safer without any overhead? I came up with a solution like this, but I am not completely sure if it is equivalent to current minmax as generated assembly is different for stl-like implementation and mine. So the question is: what are the possible problems/drawbacks of my implementation of minmax in relation to std-like one:
//below is std-like minmax
template< class T >
constexpr std::pair<const T&,const T&> std_minmax( const T& a, const T& b ){
return (b < a) ? std::pair<const T&, const T&>(b, a)
: std::pair<const T&, const T&>(a, b);
}
//below is my minmax implementation
template< class T >
constexpr std::pair<T, T> my_minmax( T&& a, T&& b ){
return (b < a) ? std::pair<T, T>(std::forward<T>(b), std::forward<T>(a))
: std::pair<T, T>(std::forward<T>(a), std::forward<T>(b));
}
Live demo at godbolt.org
As some of You claim it is unclear what I am asking, I would like to reword a bit what I want. I would like to write function which works exactly like std::minmax, but if given one temporary value - returns std::pair<T, T> instead of std::pair<const T &, const T &>. Secondly, while doing it I would like to avoid any unnecessary moving, copying of data and so forth.
I am not exactly sure what are you trying to achieve. You wrote:
without any overhead
but your solution will copy lvalue arguments. Is it what you want?
Anyway, you cannot use two forwarding references with the same template parameter this way, since it will fail if both function arguments have different categories:
template <typename T> void f(T&& a, T&& b) { }
int main() {
int a = 3;
f(a, 1); // error: template argument deduction/substitution failed
}
For the first function argument, T would be deduced as int&, and for second as int.
If you want to remove any copying, the only possibility is the member of the resulting pair to be:
a (const) lvalue reference to the corresponding function argument in case it is an lvalue,
a value moved from that argument if is it an rvalue.
I don't think this is possible to achieve. Consider:
std::string a("hello");
auto p = minmax(a, std::string("world"));
Here the resulting type would be std::pair<std::string&, std::string>. However, in case of
auto p = minmax(a, std::string("earth"));
the resulting type would be different, namely std::pair<std::string, std::string&>.
Therefore, the resulting type would depend on a runtime condition (which generally requires runtime polymorphism).
UPDATE
Out of curiosity, I just came up with a wrapper that can hold some object either by (const) pointer or by value:
template <typename T>
class val_or_ptr {
std::variant<T, const T*> v_;
public:
val_or_ptr(const T& arg) : v_(&arg) { }
val_or_ptr(T&& arg) : v_(std::move(arg)) { }
const T& get() const { return v_.index() ? *std::get<const T*>(v_) : std::get<T>(v_); }
};
With that, you can define minmax as:
template <typename T, typename U,
typename V = std::enable_if_t<std::is_same_v<std::decay_t<T>, std::decay_t<U>>, std::decay_t<T>>>
std::pair<val_or_ptr<V>, val_or_ptr<V>> minmax(T&& a, U&& b) {
if (b < a) return { std::forward<U>(b), std::forward<T>(a) };
else return { std::forward<T>(a), std::forward<U>(b) };
}
Live demo is here: https://wandbox.org/permlink/N3kdI4hzllBGFWVH
This is very basic implementation, but it should prevent copying both from lvalue and rvalue arguments of minmax.
One solution is when T is an r-value reference then copy it instead of returning an r-value reference:
#include <utility>
template<class T>
std::pair<T, T> minmax(T&& a, T&& b) {
if(a < b)
return {a, b};
return {b, a};
}
When the argument is an r-value reference T is deduced as a non-reference type:
int main() {
int a = 1;
int const b = 2;
minmax(1, 1); // std::pair<int, int>
minmax(a, a); // std::pair<int&, int&>
minmax(b, b); // std::pair<const int&, const int&>
}
With C++17 it is possible to use constexpr if to tie lvalue args and copy everything else. With C++11 I would probably think twice before building an angle brackets moster with a scary look for such a simple use case.
godbolt, coliru
template <typename T>
decltype(auto) minmax(T&& x, T&& y)
{
if constexpr(std::is_lvalue_reference_v<decltype(x)>)
return std::minmax(std::forward<T>(x), std::forward<T>(y));
else {
auto const res = std::minmax(x, y);
return std::make_pair(res.first, res.second);
}
}
To support mixed l/r values you probably need two template params, 4 cases in the if/else, and std::cref(res.xxx) as an argument to std::make_pair for partial.

std::function overloads have similar conversions

I'm in the process of writing up an STL-like library for learning purposes. All of the collections extend a class called Iterable which contains wrapping functions for all of the functions found in <algorithm>. For example, it allows vec.each([](T t){...}); which I strongly prefer over the verbose std::for_each. The function giving me problems is count - I want to overload Iterable::count so it combines the behaviour of both std::count and std::count_if depending on the argument type but I'm running into a strange error.
Iterable.h
virtual int count(const T& value) const {
return std::count(begin(), end(), value);
}
virtual int count(std::function<bool(T&)> predicate) {
return std::count_if(begin(), end(), predicate);
}
virtual int count(std::function<bool(const T&)> predicate) const {
return std::count_if(begin(), end(), predicate);
}
main.cpp
Vector<int> vec; // extends Iterable
vec.add(0);
vec.add(1);
vec.count([](int i){ return i == 0; }); // compiles and works fine
vec.count(0); // error c2666: 3 overloads have similar conversions
I should note that changing the count_if wrapper function names to count_if does work and resolves the ambiguity, but I'd prefer to have them named count and also to figure out why there is ambiguity in the first place.
From what I interpret, the compiler is trying to make a new std::function using the template <class F> function(F f) ctor, then runs into the ambiguity. Is that the case? It seems odd since the line below fails to compile as well.
std::function<bool(int)> f(0); // error C2064: term does not evaluate to a function taking 1 arguments
Any insights or potential fixes are much appreciated.
Forgot to say; using visual studio 2012, nov 2012 ctp compiler
std::function<Sig> in the published C++11 standard without errata contains a constructor that thinks it can consume anything, as far as its signature is concerned. If you pass it things it cannot consume (things that are not callable), it fails to compile.
Overload resolution occurs earlier (based on shallower information) than the compile failure. It matches on signatures, not implementations.
A bug report and a fix was proposed, so some C++11 compilers can fix this, and all C++14 compilers must fix this.
VS2012 has limited SFINAE overload resolution capabilities. But one approach would look like:
template<class Sig, class=void>
struct is_filter_on : std::false_type{};
template<class F, class Arg>
struct is_filter_on< F(Arg),
typename std::enable_if<std::is_convertible<
typename std::result_of<F(Arg)>::type
,bool>::value>::type
> : std::true_type{};
which is an attempt at a traits class that tells you if F(Arg) is a bool-returning "filter" on values of type Arg.
template<class X>
size_t count(X&& x) const {
return count( std::forward<X>(x), is_filter_on< X&(T const&) >{} );
}
template<class X>
size_t count(X&& x) {
return count( std::forward<X>(x), is_filter_on< X&(T&) >{} );
}
template<class F>
size_t count(F&& f, std::true_type) const {
return std::count_if( begin(), end(), std::forward<F>(f) );
}
template<class F>
size_t count(F&& f, std::true_type) {
return std::count_if( begin(), end(), std::forward<F>(f) );
}
template<class X>
size_t count(X&& x, std::false_type) const {
return std::count( begin(), end(), std::forward<X>(x) );
}
template<class X>
size_t count(X&& x, std::false_type) {
return std::count( begin(), end(), std::forward<X>(x) );
}
but I have no idea of MSVC2012 will work with the above.
Here I use tag dispatching to pick which version of count I call. The traits class is_filter_on does a test to determine if the pseudo-expression F(Arg) is filter-like. If so, we dispatch to the std::count_if. Otherwise, we dispatch to the std::count version.
The problem is that 0 is ambiguous here, it can be interpreted as a null pointer or an int, which makes it match both the std::function constructor and the more general const T& value (both require a conversion).
If you don't want to change the interface, you can just create a very simple function template to deduce and dispatch the arguments.
C++11 version:
template<typename U>
int count(U&& value) const {
return count_impl(std::forward<U>(value));
}
This works because the function template type deduction rules don't have that ambiguity, they never treat 0 as a null pointer.
So your interface is now:
virtual int count_impl(const T& value) const {
return std::count(v.begin(), v.end(), value);
}
virtual int count_impl(std::function<bool(T&)> predicate) {
return std::count_if(v.begin(), v.end(), predicate);
}
virtual int count_impl(std::function<bool(const T&)> predicate) const {
return std::count_if(v.begin(), v.end(), predicate);
}
template<typename U>
int count(U&& value) const {
return count_impl(std::forward<U>(value));
}
And you can use it naturally:
int main(){
Vector<int> vec; // extends Iterable
vec.count([](int i){ return i == 0; }); // compiles and works fine
vec.count(0); // no problem, calls virtual int count_impl(const T& value) const
}

template argument type deduction from std::function return type with lambda

First of, I'm using C++11 (and my topic sucks).
What I'm trying to do is write a generic template function that implements something usually called sort_by in other programming languages. It involves calculating an arbitrary criterion for each member of a range exactly once and then sorting that range according to those criteria. Such a criterion doesn't have to be a POD, all it has to be is less-than-comparable. For things for which std::less doesn't work the caller should be able to provide her own comparison functor.
I've successfully written said function which uses the following signature:
template< typename Tcriterion
, typename Titer
, typename Tcompare = std::less<Tcriterion>
>
void
sort_by(Titer first, Titer last,
std::function<Tcriterion(typename std::iterator_traits<Titer>::value_type const &)> criterion_maker,
Tcompare comparator = Tcompare()) {
}
It can be used e.g. like this:
struct S { int a; std::string b; double c; };
std::vector<S> s_vec{
{ 42, "hello", 0.5 },
{ 42, "moo!", 1.2 },
{ 23, "fubar", 0.2 },
};
sort_by1< std::pair<int, double> >(
s_vec.begin(), s_vec.end(),
[](S const &one_s) { return std::make_pair(one_s.a, one_s.c); }
);
What I don't like about this approach is that I have to provide the Tcriterion argument myself because the compiler cannot deduce that type from the lambda expression. Therefore this does not work:
sort_by1(s_vec.begin(), s_vec.end(), [](S const &one_s) { return std::make_pair(one_s.a, one_s.c); });
clang 3.1 and gcc 4.7.1 both bark on this (gcc 4.7.1 even barks on the code above, so I guess I'm really doing something wrong here).
However, if I assign the lambda to a std::function first then at least clang 3.1 can deduce the argument, meaning this works:
typedef std::pair<int, double> criterion_type;
std::function<criterion_type(S const &)> criterion_maker = [](S const &one_s) {
return std::make_pair(one_s.a, one_s.c);
};
sort_by1(s_vec.begin(), s_vec.end(), criterion_maker);
So my questions are: How do I have to change my function signature so that I don't need to specify that one argument? And (probably related) how would I fix my example to have it working with gcc?
Don't use std::function in tandem with template argument deduction. In fact, there's very likely no reason to use std::function in a function or function template argument list. More often than not, you should not use std::function; it is a very specialized tool that is very good at solving one particular problem. The rest of the time, you can dispense with it altogether.
In your case you don't need template argument deduction if you use a polymorphic functor to order things:
struct less {
template<typename T, typename U>
auto operator()(T&& t, U&& u) const
-> decltype( std::declval<T>() < std::declval<U>() )
{ return std::forward<T>(t) < std::forward<U>(u); }
// operator< is not appropriate for pointers however
// the Standard defines a 'composite pointer type' that
// would be very helpful here, left as an exercise to implement
template<typename T, typename U>
bool operator()(T* t, U* u) const
{ return std::less<typename std::common_type<T*, U*>::type> {}(t, u); }
};
You can then declare:
template<typename Iter, typename Criterion, typename Comparator = less>
void sort_by(Iter first, Iter last, Criterion crit, Comparator comp = less {});
and comp(*ita, *itb) will do the right thing, as well as comp(crit(*ita), crit(*itb)) or anything else as long as it makes sense.
How about something like this:
template< typename Titer
, typename Tmaker
, typename Tcompare
>
void
sort_by(Titer first, Titer last,
Tmaker criterion_maker,
Tcompare comparator)
{
typedef decltype(criterion_maker(*first)) Tcriterion;
/*
Now that you know the actual type of your criterion,
you can do the real work here
*/
}
The problem is that you can obviously not use a default for the comparator with this, but you can easily overcome that by providing an overload that doesn't take a comparator and fills in std::less internally.
To do it like you originally suggested, the compiler would have to be able to "invert" the template instantiation process. I.e. for a given std::function<> instantiation, what parameter do I have to supply as the result to get it. This "looks" easy, but it is not!
You can use also something like this.
template< typename Titer
, typename Tmaker
, typename TCriterion = typename
std::result_of
<
Tmaker
(
decltype(*std::declval<Titer>())
)
>::type
, typename Tcompare = std::less<TCriterion>
>
void
sort_by(Titer first, Titer last,
Tmaker criterion_maker, Tcompare comparator = Tcompare())
{
}
http://liveworkspace.org/code/0aacc8906ab4102ac62ef0e45a37707d