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