How can I use std::lcm
with std::ranges::fold_left
with C++23 GCC 13.2.0/ what is perhaps a better way?
#include <algorithm>
#include <initializer_list>
#include <numeric>
int main() {
unsigned int a = std::ranges::fold_left(std::initializer_list<unsigned int>{3U, 5U}, 2U, std::lcm<unsigned int>);
return 0;
}
The above code fails in GCC 13.2.0 and at cppinsights.io, which apparently uses the same compiler, with the same result:
/home/insights/insights.cpp:6:22: error: no matching function for call to object of type 'const __fold_left_fn'
6 | unsigned int a = std::ranges::fold_left(std::initializer_list<unsigned int>{3U, 5U}, 2U, std::lcm<unsigned int>);
| ^~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/ranges_algo.h:3821:7: note: candidate template ignored: couldn't infer template argument '_Fp'
3821 | operator()(_Range&& __r, _Tp __init, _Fp __f) const
| ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/ranges_algo.h:3812:7: note: candidate function template not viable: requires 4 arguments, but 3 were provided
3812 | operator()(_Iter __first, _Sent __last, _Tp __init, _Fp __f) const
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
Error while processing /home/insights/insights.cpp.
After checking the example at cppreference.com, I compared the signature of std::plus
and std::multiplies
with std::lcm
and found (more or less) the following:
template< class M, class N >
constexpr std::common_type_t<M, N> lcm( M m, N n );
template< class T = void >
struct plus {
constexpr T operator()( const T& lhs, const T& rhs ) const {
return lhs + rhs;
}
};
template< class T = void >
struct multiplies {
constexpr T operator()( const T& lhs, const T& rhs ) const {
return lhs * rhs;
}
};
This for sure explains the issue.
What is the recommended/ "standard" way?
Is the idea really that I implement a struct myself?
And why are the interfaces this inconsistent?
Given a function with the signature template< class M, class N > constexpr std::common_type_t<M, N> lcm( M m, N n );
, lcm<unsigned int>
would not be enough to pick out a single overload.
You would have to provide both template arguments (lcm<unsigned int, unsigned int>
) or disambiguate it another way like static_cast<unsigned int(*)(unsigned int, unsigned int)>(lcm)
.
However, std::lcm<unsigned, unsigned>
is not an addressable function. This means you are not allowed to pass its address to another function.
The recommended way is to make your own struct. That can easily be done with a lambda:
unsigned a = std::ranges::fold_left(std::initializer_list<unsigned>{3U, 5U}, 2U, [](unsigned m, unsigned n) {
return std::lcm(m, n);
});