Sum the components of a tuple up by using std::get, std::tuple_size, std::tuple_element

前端 未结 2 1474
深忆病人
深忆病人 2021-01-06 04:45

I\'ve got a custom class that has a tuple-like interface. Because I want my code to be as generic as possible, I thought that it would be a good idea to base my algorithms o

相关标签:
2条回答
  • 2021-01-06 05:20

    This is very easy in 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 );
    };
    

    or (...+e) for the opposite fold direction.

    In previous versions, the right approach would be to write your own apply rather than writing a bespoke implementation. When your compiler updates, you can then delete code.

    In c++14, I might do this:

    // 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) )... );
        } ); 
      }
    }
    

    which is pretty close to std::apply in c++14. (I abuse std::ref to get INVOKE semantics). (It does not work perfectly with rvalue invokers, but that is very corner case).

    In c++11, I would advise upgrading your compiler at this point. In c++03 I'd advise upgrading your job at this point.


    All of the above do right or left folds. In some cases, a binary tree fold might be better. This is trickier.

    If your + does expression templates, the above code won't work well due to lifetime issues. You may have to add another template type for "afterwards, cast-to" to cause the temporary expression tree to evaluate in some cases.

    0 讨论(0)
  • 2021-01-06 05:23

    With C++1z it's pretty simple with fold expressions. First, forward the tuple to an _impl function and provide it with index sequence to access all tuple elements, then sum:

    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


    A C++14 approach would be to recursively sum a variadic pack:

    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

    A C++11 approach would be the C++14 approach with custom implementation of index_sequence. For example from here.


    As @ildjarn pointed out in the comments, the above examples are both employing right folds, while many programmers expect left folds in their code. The C++1z version is trivially changeable:

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

    demo

    And the C++14 isn't much worse, but there are more changes:

    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

    0 讨论(0)
提交回复
热议问题