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

Max*_*Max 4 c++ tuples boost-tuples stdtuple c++17

我有一个具有类似元组的界面的自定义类.因为我想我的代码尽可能通用的,我认为这将是我的基础上的功能的算法是一个好主意std::get,std::tuple_size,std::tuple_element所以你只需要专注这些功能用我的算法.让我们称之为需要这些功能特化的概念Tuple.

现在我试图总结一个组件Tuple.函数声明应该是这样的:

template <class Tuple>
int sum_components(const Tuple& t);
Run Code Online (Sandbox Code Playgroud)

我想有很多模板编程涉及但我无法弄清楚如何做到这一点.

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

我正在使用c ++ 1z.

Yak*_*ont 10

这在非常简单.

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 );
};
Run Code Online (Sandbox Code Playgroud)

(...+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) )... );
    } ); 
  }
}
Run Code Online (Sandbox Code Playgroud)

这是非常接近std::apply.(我滥用std::ref来获取INVOKE语义).(它与rvalue调用者无法完美配合,但这种情况非常糟糕).

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


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

如果您+使用表达式模板,由于生命周期问题,上述代码将无法正常运行.您可能必须为"afterwards,cast-to"添加另一个模板类型,以便在某些情况下评估临时表达式树.


krz*_*zaq 5

使用C++ 1z,使用折叠表达式非常简单.首先,将元组转发给_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>{});
}
Run Code Online (Sandbox Code Playgroud)

演示


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>{});
}
Run Code Online (Sandbox Code Playgroud)

演示

C++ 11方法将是C++ 14方法,具有自定义实现index_sequence.例如从这里开始.


正如@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));
}
Run Code Online (Sandbox Code Playgroud)

演示

并且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)...);
}
Run Code Online (Sandbox Code Playgroud)

演示