使用 std::get、std::tuple_size、std::tuple_element 对元组的组件求和
Sum the components of a tuple up by using std::get, std::tuple_size, std::tuple_element
我有一个自定义 class,它具有类似元组的界面。因为我希望我的代码尽可能通用,所以我认为将我的算法基于函数 std::get
、std::tuple_size
、std::tuple_element
是个好主意,所以您只需要专门化这些功能以使用我的算法。我们将需要这些函数特化的概念称为 Tuple
.
现在我正在尝试总结 Tuple
的组成部分。函数声明应该是这样的:
template <class Tuple>
int sum_components(const Tuple& t);
我想这涉及到很多模板编程,但我不知道如何去做。
对于添加,我将只使用全局 + operator
.
的重载
我正在使用 c++1z。
对于 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>{});
}
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>{});
}
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));
}
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)...);
}
这在 c++17 中非常容易。
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
而不是编写定制的实现。当您的编译器更新时,您可以删除代码。
在c++14,我可能会这样做:
// 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) )... );
} );
}
}
与 c++14 中的 std::apply
非常接近。 (我滥用 std::ref
来获得 INVOKE
语义)。 (它不能与右值调用器完美配合,但这是非常极端的情况)。
在 c++11, I would advise upgrading your compiler at this point. In c++03 我建议此时升级你的工作。
以上所有的都做右折或左折。在某些情况下,二叉树折叠可能会更好。这比较棘手。
如果您的 +
使用表达式模板,由于生命周期问题,上述代码将无法正常工作。在某些情况下,您可能必须为 "afterwards, cast-to" 添加另一个模板类型才能使临时表达式树求值。
我有一个自定义 class,它具有类似元组的界面。因为我希望我的代码尽可能通用,所以我认为将我的算法基于函数 std::get
、std::tuple_size
、std::tuple_element
是个好主意,所以您只需要专门化这些功能以使用我的算法。我们将需要这些函数特化的概念称为 Tuple
.
现在我正在尝试总结 Tuple
的组成部分。函数声明应该是这样的:
template <class Tuple>
int sum_components(const Tuple& t);
我想这涉及到很多模板编程,但我不知道如何去做。
对于添加,我将只使用全局 + operator
.
我正在使用 c++1z。
对于 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>{});
}
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>{});
}
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));
}
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)...);
}
这在 c++17 中非常容易。
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
而不是编写定制的实现。当您的编译器更新时,您可以删除代码。
在c++14,我可能会这样做:
// 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) )... );
} );
}
}
与 c++14 中的 std::apply
非常接近。 (我滥用 std::ref
来获得 INVOKE
语义)。 (它不能与右值调用器完美配合,但这是非常极端的情况)。
在 c++11, I would advise upgrading your compiler at this point. In c++03 我建议此时升级你的工作。
以上所有的都做右折或左折。在某些情况下,二叉树折叠可能会更好。这比较棘手。
如果您的 +
使用表达式模板,由于生命周期问题,上述代码将无法正常工作。在某些情况下,您可能必须为 "afterwards, cast-to" 添加另一个模板类型才能使临时表达式树求值。