I ran into a portability issue with std::reduce()
.
There’s a call to std::reduce()
that compiles in Visual Studio, but not with gcc. It’s not clear to me from the cpp documentation if the issue is a bug in gcc, or if Visual Studio is not respecting the spec.
I am using an initial integer value. I want to reduce from a container of pairs, and am using a custom binary operator with signature int (int &&, const std::pair<int, bool> &)
.
The code given below contains my version of reduce()
in the MyImpl
namespace. The code provides a switch to use either my version or the std
version.
Could someone, please, take a quick look at the source code and let me know what is it that I am doing wrong?
#include <iostream>
#include <algorithm>
#include <numeric>
#include <vector>
namespace MyImpl
{
template <typename IIter, typename T, typename BinaryOp>
T reduce(IIter first, IIter last, T val, const BinaryOp &bOp)
{
for (; first != last; ++first)
val = bOp(std::move (val), *first);
return val;
}
}
int main ()
{
std::vector<std::pair<int, bool>> vv {std::pair{1, false}, std::pair{2, true}, std::pair{-1, false}, std::pair{7, true}, std::pair{10, false}, };
#define MY_IMPLEMENTATION
#ifdef MY_IMPLEMENTATION
// I get the results that I expect using my implementation of reduce
std::cout << MyImpl::reduce (vv.begin (), vv.end (), 0, [] (auto &&value, const auto &ve)
{
return (ve.second ? (value + ve.first) : (value));
});
#else
// std's implementation gives an error
std::cout << std::reduce (vv.begin (), vv.end (), 0, [] (auto &&value, const auto &ve)
{
return (ve.second ? (value + ve.first) : (value));
});
#endif
return 0;
}
Here’s the error –
By the way, if you click on the "source code" link, you will see the Coliru code.
main.cpp: In instantiation of 'main()::<lambda(auto:16&&, const auto:17&)> [with auto:16 = std::pair<int, bool>&; auto:17 = int]':
/usr/local/include/c++/12.1.0/type_traits:2565:26: required by substitution of 'template<class _Fn, class ... _Args> static std::__result_of_success<decltype (declval<_Fn>()((declval<_Args>)()...)), std::__invoke_other> std::__result_of_other_impl::_S_test(int) [with _Fn = main()::<lambda(auto:16&&, const auto:17&)>&; _Args = {std::pair<int, bool>&, int&}]'
/usr/local/include/c++/12.1.0/type_traits:2576:55: required from 'struct std::__result_of_impl<false, false, main()::<lambda(auto:16&&, const auto:17&)>&, std::pair<int, bool>&, int&>'
/usr/local/include/c++/12.1.0/type_traits:3050:12: recursively required by substitution of 'template<class _Result, class _Ret> struct std::__is_invocable_impl<_Result, _Ret, false, std::__void_t<typename _CTp::type> > [with _Result = std::__invoke_result<main()::<lambda(auto:16&&, const auto:17&)>&, std::pair<int, bool>&, int&>; _Ret = int]'
/usr/local/include/c++/12.1.0/type_traits:3050:12: required from 'struct std::is_invocable_r<int, main()::<lambda(auto:16&&, const auto:17&)>&, std::pair<int, bool>&, int&>'
/usr/local/include/c++/12.1.0/type_traits:3292:44: required from 'constexpr const bool std::is_invocable_r_v<int, main()::<lambda(auto:16&&, const auto:17&)>&, std::pair<int, bool>&, int&>'
/usr/local/include/c++/12.1.0/numeric:283:21: required from 'constexpr _Tp std::reduce(_InputIterator, _InputIterator, _Tp, _BinaryOperation) [with _InputIterator = __gnu_cxx::__normal_iterator<pair<int, bool>*, vector<pair<int, bool> > >; _Tp = int; _BinaryOperation = main()::<lambda(auto:16&&, const auto:17&)>]'
ma
2
Answers
My question has been answered! Thanks!
Per cppreference:
https://en.cppreference.com/w/cpp/algorithm/reduce
That means there are 4 possible ways for the binary operator to be called by
std::reduce()
, but your lambdas are only accounting for one of them:binary_op(init, *first)
, which works fine forMyImpl::reduce()
because that is how it is calling the operator. But that is not guaranteed forstd::reduce()
, as you can clearly see in the very 1st line of the error message:Your
std::reduce()
lambda is expecting to be called asbinary_op(init, *first)
, butstd::reduce()
is actually calling it asbinary_op(*first, init)
instead.So, you may need something more like this instead: