Reputation: 3984
I am confused about the following differences when using with algorithms.
First of all, algorithms work with lambda, functions and functors without any issue. For example,
bool comp (int t1, int t2) {
return t1 > t2;
}
std::array<int, 10> myArray{5, -10, 3, 2, 7, 8, 9, -4, 3, 4};
std::sort(myArray.begin(), myArray.end(), comp);
Now let's define templated comparators,
template<typename T>
bool comp1(T t1, T t2) {
return t1 > t2;
}
struct Comp {
template<typename T>
bool operator()(T t1, T t2) const {
return t1 > t2;
}
};
Comp comp2;
auto comp3 = [](auto t1, auto t2) { return t1 > t2; }
template<typename Container>
void sortDescending(Container &c) {
std::sort(c.begin(), c.end(), comp1); // cannot deduce argument error
std::sort(c.begin(), c.end(), comp2); // works
std::sort(c.begin(), c.end(), comp3); // works
}
sortDescending(myArray);
The function template does not work in the above scenario, which makes me confused.
To me, at least the comp1 and comp2 are almost the same (in the sense that the amount of information required to deduce the argument). But comp1 doesn't work.
So my question is, what is the ultimate reason behind this difference?
I.e., what are the key differences between a function template, a template operator() (template functor?), and a generic lambda in terms of argument deduction?
Thanks.
Upvotes: 2
Views: 112
Reputation: 474266
A template is a language construct of C++ which generates other constructs: classes, functions, or variables (in C++14). The name of a template, without its template parameters, can only be used in very limited ways.
comp1
names a template, not a function. That's the important point. A function template is a construct that generates a function when you provide specific template parameters; comp1<int>
is the name of a function. But the name by itself names the template, not a function it generates.
The name of a function template can be called (ie: comp1(arg1, arg2)
) because of a special rule of C++ called "template argument deduction". In short, the compiler deduces the types of the template arguments to comp
based on the declaration of comp
and the nature of the arguments provided to the function call. But this only works because C++ has a rule that says that it does.
The name of a template alone (like comp1
) is not an expression, and a function call expects expressions (or braced-init-lists) to be given as arguments. The error "cannot deduce argument error" is not about deduction within std::sort
; it's about deducing the template arguments for std::sort
itself. comp1
, a template, has no type, so it cannot participate in argument deduction.
comp2
names a variable, as does comp3
. Both of these are expressions, so both can be passed to functions.
Yes, the types of both comp2
and comp3
contain a function template. But comp2
and comp3
as names name variables, not the function templates they contain. So they work like any other variable.
As for a generic lambda, that is no different from any other user-defined type with a template operator()
method.
Upvotes: 4
Reputation: 173004
comp1
is a template; while Comp
and the lambda closure type are not, they just have template operators. Template argument deduction is performed inside std::sort
when functors are called with arguments, not at the time when they're passed to std::sort
. In the 1st case, there's no such function arguments to be used to deduce for comp1
, you have to specify the template argument explicitly, e.g.
std::sort(c.begin(), c.end(), comp1<typename Container::value_type>);
Upvotes: 1