我遇到了
std::reduce()
的便携性问题。
调用
std::reduce()
在 Visual Studio 中编译,但不使用 gcc。如果问题是 gcc 中的错误,或者 Visual Studio 是否不遵守规范,我不清楚 cpp 文档。
我正在使用一个初始整数值。我想从成对的容器中减少,并且正在使用带有签名
int (int &&, const std::pair<int, bool> &)
.的自定义二元运算符
下面给出的代码在
reduce()
命名空间中包含我的 MyImpl
版本。该代码提供了一个开关,可以使用我的版本或 std
版本。
有人可以快速浏览一下源代码,让我知道我做错了什么吗?
#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;
}
这里是错误-
顺便说一句,如果你点击“源代码”链接,你会看到 Coliru 代码。
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
个人偏好:
https://en.cppreference.com/w/cpp/algorithm/reduce
必须满足 MoveConstructible 的要求。和T
、binary_op(init, *first)
、binary_op(*first, init)
和binary_op(init, init)
必须可转换为binary_op(*first, *first)
.T
这意味着 std::reduce()
调用二元运算符有
4 种可能的方式,但您的 lambda 只占其中之一:
binary_op(init, *first)
,它适用于 MyImpl::reduce()
因为它就是这样呼叫接线员。但这并不能保证std::reduce()
,正如您在错误消息的第一行中清楚地看到的那样:
main.cpp: 在 'main()::
[with auto:16 = std::pair &; auto:17 = int]':
您的
std::reduce()
lambda 期望被称为 binary_op(init, *first)
,但 std::reduce()
实际上将其称为 binary_op(*first, init)
。
所以,您可能需要更多类似的东西:
template<typename T>
inline constexpr bool is_int_v = std::is_same_v<std::remove_cvref_t<T>, int>;
std::cout << std::reduce (vv.begin (), vv.end (), 0,
[] (const auto &arg1, const auto &arg2)
{
if constexpr (is_int_v<decltype(arg1)>)
{
if constexpr (is_int_v<decltype(arg2)>)
{
// binary_op(init, init)
return arg1;
}
else
{
// binary_op(init, *first)
return arg1 + (arg2.second ? arg2.first : 0);
}
}
else if constexpr (is_int_v<decltype(arg2)>)
{
// binary_op(*first, init)
return arg2 + (arg1.second ? arg1.first : 0);
}
else
{
// binary_op(*first, *first)
return arg1.second ? arg1.first : 0;
}
}
);