使用std :: get,std :: tuple_size,std :: tuple_element对元组的组件求和

时间:2016-11-20 02:48:45

标签: c++ tuples c++17 stdtuple boost-tuples

我有一个具有类似元组的界面的自定义类。因为我希望我的代码尽可能通用,所以我认为将算法基于函数std::getstd::tuple_sizestd::tuple_element是一个好主意所以你只需要专门化这些功能来使用我的算法。让我们调用需要这些函数特化Tuple的概念。

现在我想总结一下Tuple的组成部分。函数声明应该是这样的:

template <class Tuple>
int sum_components(const Tuple& t);

我想有很多模板编程,但我无法弄清楚如何去做。

对于添加,我只会使用全局+ operator的重载。

我正在使用c ++ 1z。

2 个答案:

答案 0 :(得分:10)

非常容易。

>>> '   spacious   '.rstrip()
'   spacious'
>>> "AABAA".rstrip("A")
  'AAB'
>>> "ABBA".rstrip("AB") # both AB and BA are stripped
   ''
>>> "ABCABBA".rstrip("AB")
   'ABC'
对于相反的折叠方向,

template<class Tuple> decltype(auto) sum_components(Tuple const& tuple) { auto sum_them = [](auto const&... e)->decltype(auto) { return (e+...); }; return std::apply( sum_them, tuple ); };

在以前的版本中,正确的方法是编写自己的(...+e)而不是编写定制的实现。当您的编译器更新时,您可以删除代码。

中,我可能会这样做:

apply

非常靠近中的// namespace for utility code: namespace utility { template<std::size_t...Is> auto index_over( std::index_sequence<Is...> ) { return [](auto&&f)->decltype(auto){ return decltype(f)(f)( std::integral_constant<std::size_t,Is>{}... ); }; } template<std::size_t N> auto index_upto() { return index_over( std::make_index_sequence<N>{} ); } } // namespace for semantic-equivalent replacements of `std` code: namespace notstd { template<class F, class Tuple> decltype(auto) apply( F&& f, Tuple&& tuple ) { using dTuple = std::decay_t<Tuple>; auto index = ::utility::index_upto< std::tuple_size<dTuple>{} >(); return index( [&](auto...Is)->decltype(auto){ auto target=std::ref(f); return target( std::get<Is>( std::forward<Tuple>(tuple) )... ); } ); } } 。 (我滥用std::apply来获取std::ref语义。 (它与rvalue调用者不能很好地协同工作,但这种情况非常糟糕。)

中,我建议此时升级您的编译器。在中,我建议此时升级您的工作。

以上所有做左右折叠。在某些情况下,二叉树折叠可能更好。这比较棘手。

如果您的INVOKE执行表达式模板,则由于生命周期问题,上述代码无法正常运行。您可能必须为&#34;之后添加另一个模板类型,cast-to&#34;在某些情况下使临时表达式树得到评估。

答案 1 :(得分:5)

使用C ++ 1z,fold expressions非常简单。首先,将元组转发给_impl函数,并为其提供索引序列以访问所有元组元素,然后求和:

template<typename T, size_t... Is>
auto sum_components_impl(T const& t, std::index_sequence<Is...>)
{
    return (std::get<Is>(t) + ...);
}

template <class Tuple>
int sum_components(const Tuple& t)
{
    constexpr auto size = std::tuple_size<Tuple>{};
    return sum_components_impl(t, std::make_index_sequence<size>{});
}

demo

C ++ 14的方法是以递归方式对可变参数包进行求和:

int sum()
{
    return 0;
}

template<typename T, typename... Us>
auto sum(T&& t, Us&&... us)
{
    return std::forward<T>(t) + sum(std::forward<Us>(us)...);
}

template<typename T, size_t... Is>
auto sum_components_impl(T const& t, std::index_sequence<Is...>)
{
    return sum(std::get<Is>(t)...);
}

template <class Tuple>
int sum_components(const Tuple& t)
{
    constexpr auto size = std::tuple_size<Tuple>{};
    return sum_components_impl(t, std::make_index_sequence<size>{});
}

demo

C ++ 11方法是自定义实现index_sequence的C ++ 14方法。例如,来自here

正如@ildjarn在评论中指出的那样,上面的例子都采用了正确的折叠,而许多程序员都期望在他们的代码中留下折叠。 C ++ 1z版本可以轻易改变:

template<typename T, size_t... Is>
auto sum_components_impl(T const& t, std::index_sequence<Is...>)
{
    return (... + std::get<Is>(t));
}

demo

C ++ 14并没有更糟糕,但还有更多变化:

template<typename T, typename... Us>
auto sum(T&& t, Us&&... us)
{
    return sum(std::forward<Us>(us)...) + std::forward<T>(t);
}

template<typename T, size_t... Is>
auto sum_components_impl(T const& t, std::index_sequence<Is...>)
{
    constexpr auto last_index = sizeof...(Is) - 1;
    return sum(std::get<last_index - Is>(t)...);
}

demo