std::reduce 在容器类型和初始值不同时编译失败

问题描述 投票:0回答:1

我遇到了

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
c++ numeric
1个回答
3
投票

个人偏好:

https://en.cppreference.com/w/cpp/algorithm/reduce

T
必须满足 MoveConstructible 的要求。和
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;
        }
    }
);
© www.soinside.com 2019 - 2024. All rights reserved.