积累价值元组

时间:2013-07-18 23:54:24

标签: c++ c++11

我正在玩元组和模板。我知道如果练习你会使用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;
}

3 个答案:

答案 0 :(得分:4)

我不知道你是否感兴趣,但是如果你能稍微提升一下,你就可以“开箱即用”了:

#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

答案 1 :(得分:2)

你不能让编译器匹配依赖于嵌套类型的模板特化,例如{Partial specialization with type nested in a templated class中的std::tuple_size<T>::value,所以你必须找到让编译器知道的替代方法当递归结束时。

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

#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(预发行版)成功编译和测试。

答案 2 :(得分:0)

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

以下是使用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;
   }
};

类型特征对于限制模板函数/ class / functor中要接受的类型非常重要

更新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;
   }
};