【问题标题】:accumulate over tuple of values累积值的元组
【发布时间】:2013-09-06 11:32:33
【问题描述】:

我在玩元组和模板。我知道如果练习你会使用 boost::fusion (我认为)来做这种事情。我正在尝试在元组上实现 std::accumulate 的等价物。

下面是我的代码。据我所知,编译错误是由它尝试使用 4 模板参数版本引起的,而我打算使用 3 模板参数版本来完成递归。这意味着我错过了函数重载决议。

我原以为由于两个函数都可以匹配,所以它会选择 3 个模板参数版本作为更好的匹配,因为最后一个参数类型已明确说明。如果我将 std::tuple_size 作为附加模板参数添加到两个版本的 tuple_accumulate_helper,我仍然会得到相同的行为。

谁能建议我做错了什么?

#include <tuple>

template <std::size_t I>
struct int_{};

template <typename T, typename OutT, typename OpT, std::size_t IndexI>
auto tuple_accumulate_helper(T tuple, OutT init, OpT op, int_<IndexI>) -> decltype(tuple_accumulate_helper(tuple, op(init, std::get<IndexI>(tuple)), op, int_<IndexI + 1>()))
{
   return tuple_accumulate_helper(tuple, op(init, std::get<IndexI>(tuple)), op, int_<IndexI + 1>());
}

template <typename T, typename OutT, typename OpT>
auto tuple_accumulate_helper(T tuple, OutT init, OpT op, int_<std::tuple_size<T>::value>) -> decltype(init)
{
   return init;
}

template <typename T, typename OutT, typename OpT>
auto tuple_accumulate(T tuple, OutT init, OpT op) -> decltype(tuple_accumulate_helper(tuple, init, op, int_<0>()))
{
   return tuple_accumulate_helper(tuple, init, op, int_<0>());
}

struct functor
{
   template <typename T1, typename T2>
   auto operator()(T1 t1, T2 t2) -> decltype(t1 + t2)
   {
      return t1 + t2;
   }
};

int main(int argc, const char* argv[])
{
   auto val = tuple_accumulate(std::make_tuple(5, 3.2, 7, 6.4f), 0, functor());
   return 0;
}

【问题讨论】:

  • VS 是合规性落后者。使用coliru.stacked-crooked.comideone.com 等在线编译器从更多最新的编译器安装中获取意见。根据 Clang 的说法,你有一个索引溢出错误,使用 index = 4 进入一个大小为 4 的元组。VC 说了什么?
  • 你的代码看起来不错,除了 int_<:tuple_size>::value> 应该是 int_<:tuple_size>::value–1>。它没有在 gcc4.8.1 上编译。它适用于 msvc2013 v120。
  • 通过 clang 运行它似乎当我期望它选择具有 3 个模板参数的重载时,它正在选择具有 4 个模板参数的重载,然后未能实例化 std::get 过去元组的结尾。

标签: c++ c++11


【解决方案1】:

我不知道你是否感兴趣,但如果你可以使用一点提升,你可以“开箱即用”:

#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/include/algorithm.hpp>
#include <boost/phoenix/phoenix.hpp>

using namespace boost::phoenix::arg_names;

#include <iostream>

int main()
{
    auto t = std::make_tuple(5, 3.2, 7, 6.4f); 

    std::cout << boost::fusion::accumulate(t, 0, arg1 + arg2) << std::endl;
    std::cout << boost::fusion::accumulate(t, 1, arg1 * arg2) << std::endl;
}

打印

21.6
716.8

【讨论】:

    【解决方案2】:

    您无法让编译器匹配依赖于嵌套类型的模板特化,在您的示例中为 std::tuple_size&lt;T&gt;::value [Partial specialization with type nested in a templated class],因此您必须找到其他方法让编译器知道递归何时结束.

    在下面的代码 sn-p 中,我提供了一种替代方法。我绝不声称我的解决方案在任何方面都是“最好的”,但我相信它可能有助于向您展示如何解决这个问题:

    #include <cstddef>
    #include <tuple>
    #include <utility>
    #include <type_traits>
    #include <iostream>
    
    
    //deduces the type resulted from the folding of a sequence from left to right
    //avoids the decltype nonsense
    template <typename T, typename OpT>
    class result_of_acumulate;
    
    template <typename... ArgsT, typename OpT>
    class result_of_acumulate<std::tuple<ArgsT...>, OpT>
    {
    private:
        template <typename... ArgsHeadT>
        struct result_of_acumulate_helper;
    
        template <typename ArgsHeadT>
        struct result_of_acumulate_helper<ArgsHeadT>
        {
            typedef ArgsHeadT type;
        };
    
        template <typename ArgsHead1T, typename ArgsHead2T>
        struct result_of_acumulate_helper<ArgsHead1T, ArgsHead2T>
        {
            typedef typename std::result_of<OpT(ArgsHead1T, ArgsHead2T)>::type type;
        };
    
        template <typename ArgsHead1T, typename ArgsHead2T, typename... ArgsTailT>
        struct result_of_acumulate_helper<ArgsHead1T, ArgsHead2T, ArgsTailT...>
        {
            typedef typename result_of_acumulate_helper<typename std::result_of<OpT(ArgsHead1T, ArgsHead2T)>::type, ArgsTailT...>::type type;
        };
    
    public:
        typedef typename result_of_acumulate_helper<ArgsT...>::type type;
    };
    
    template <std::size_t IndexI, typename T, typename OutT, typename OpT>
    constexpr typename std::enable_if<(IndexI == std::tuple_size<T>::value), OutT>::type
    tuple_accumulate_helper(T const& /*tuple*/, OutT const& init, OpT /*op*/)
    {
        return init;
    }
    
    template <std::size_t IndexI, typename T, typename OutT, typename OpT>
    constexpr typename std::enable_if
    <
        (IndexI < std::tuple_size<T>::value),
        typename result_of_acumulate<T, OpT>::type
    >::type
    tuple_accumulate_helper(T const& tuple, OutT const init, OpT op)
    {
        return tuple_accumulate_helper<IndexI + 1>(tuple, op(init, std::get<IndexI>(tuple)), op);
    }
    
    template <typename T, typename OutT, typename OpT>
    auto tuple_accumulate(T const& tuple, OutT const& init, OpT op)
        -> decltype(tuple_accumulate_helper<0>(tuple, init, op))
    {
        return tuple_accumulate_helper<0>(tuple, init, op);
    }
    
    struct functor
    {
        template <typename T1, typename T2>
        auto operator()(T1 t1, T2 t2)
            -> decltype(t1 + t2)
        {
            return t1 + t2;
        }
    };
    
    int main(int /*argc*/, const char* /*argv*/[])
    {
        auto val = tuple_accumulate(std::make_tuple(5, 3.2, 7U, 6.4f), 0L, functor());
        std::cout << val << std::endl; //should output 21.6
        return 0;
    }
    

    在 Archlinux x64 机器上使用 gcc (GCC) 4.8.1 20130725(预发行版)成功编译和测试。

    【讨论】:

    • +1 因为它可以工作,而编译时间是我的 boost 示例所需时间的 1/10:/
    【解决方案3】:

    一个建议:因为 std::tuples 可以携带非数字类型,你应该让你的仿函数意识到这种可能性并在编译时警告你。

    这是一个使用 SFINAE 的建议

    struct functor
    {
       template <typename T1, typename T2,
       typename std::enable_if< std::is_arithmetic<T1>::value == true && 
       std::is_arithmetic<T2>::value == true , bool>::type = false >
       auto operator()(T1 t1, T2 t2) -> decltype(t1 + t2)
       {
          return t1 + t2;
       }
    };
    

    类型特征对于限制您希望在模板函数/类/函子中接受的类型非常重要

    更新 1:另一个使用 static_assert 的建议(触发时会给出明确的错误消息)

    struct functor
    {
       template <typename T1, typename T2 >
       auto operator()(T1 t1, T2 t2) -> decltype(t1 + t2)
       {
          static_assert( std::is_arithmetic<T1>::value == true && 
       std::is_arithmetic<T2>::value == true )
          return t1 + t2;
       }
    };
    

    【讨论】:

      猜你喜欢
      • 2015-11-27
      • 1970-01-01
      • 2019-05-31
      • 2019-05-04
      • 2014-01-03
      • 1970-01-01
      • 2021-06-19
      • 1970-01-01
      • 2022-11-02
      相关资源
      最近更新 更多