Sorry for the long title, as I stated this works:
#include <algorithm>
#include <iostream>
#include <vector>
struct S { int a{}, b{}; };
std::ostream &operator<<(std::ostream &o, const S &s) { return o << '{' << s.a << ',' << s.b << '}'; }
template <auto ptr>
bool f(const S &a, const S &b)
{
return a.*ptr < b.*ptr;
}
int main()
{
std::vector<S> v{{1,9},{2,8},{3,7},{4,6},{6,4},{7,3},{8,2},{9,1}};
std::sort(v.begin(), v.end(), f<&S::b>);
// Sort by S::b -----> ^^^^^
for (const auto &s : v)
std::cout << s << ' ';
// output: {9,1} {8,2} {7,3} {6,4} {4,6} {3,7} {2,8} {1,9}
return 0;
}
But this don't:
…
auto l = []<auto ptr>(const S &a, const S &b)
{
return a.*ptr < b.*ptr;
};
std::sort(v.begin(), v.end(), l<&S::b>);
// Sort by S::b? -----> ^^^^^
…
This is the error:
error: no match for 'operator<' (operand types are 'main()::<lambda(const S&, const S&)>' and 'int S::*')
std::sort(v.begin(), v.end(), l<&S::b>);
~^~~~~~
Looks like the template notation for the lambda as parameter for std::sort
is taken as a comparison of l
less than (<
) &S::b>
, that wasn't my intention.
How should I pass the template lambda to std::sort
function?
You can just do this: std::ranges::sort(v, {}, &S::b)
.
Your version doesn't work because l
is not a template. It's a class instance with a templated operator()
.
&decltype(l)::operator()<&S::b>
would compile, but the resulting member pointer is not something that std::sort
can work with.
It will work if you make the lambda static
([](...) static {...}
, a C++23 feature), or if you wrap that expression in std::bind_front(..., l)
or equivalent.
Honestly, if you don't want to use std::ranges::sort()
for some reason, I'd do this:
auto l = [](auto ptr)
{
return [ptr](const S &a, const S &b)
{
return a.*ptr < b.*ptr;
};
};
std::sort(v.begin(), v.end(), l(&S::b));
Yes, this results in a stateful comparator (the member pointer isn't embedded into the type), but the syntax looks much nicer, and this should hopefully be optimized away.