1

Given a

struct A{}; struct B{}; struct C{};
std::tuple<A,B,C> tabc;

How to drop second element B from it to get tuple<A,C>, like

std::tuple<A,C> tac = drop<B>(tabc);

or the same

std::tuple<A,C> tac = drop<1>(tabc);

I assume this produces a new type with copies of elements.

Tony Tannous
  • 14,154
  • 10
  • 50
  • 86
kyb
  • 7,233
  • 5
  • 52
  • 105

4 Answers4

2

Check out this answer how to make a subtuple:

Creating a sub-tuple starting from a std::tuple<some_types...>

Read it. By making a subtuple routine you can make a generic version of drop:

 template<size_t index, typename Tuple>
 auto Drop(const Tuple& t)
 {
     return std::tuple_cat(
         subtuple_v2<0,index>(t), 
         subtuple_v2<index+1, std::tuple_size<Tuple>::value>(t));
 }

Where subtuple_v2<a,b> is an extended version from the post that generates subtuple from indexes a to b.

cigien
  • 57,834
  • 11
  • 73
  • 112
ALX23z
  • 4,456
  • 1
  • 11
  • 18
2

This is fairly simple enough to code with meta-programming techniques:

template<size_t drop, size_t ...ixs>
constexpr auto calc_drop_sequence_dropper(std::index_sequence<ixs...>)
{
    return std::index_sequence<(ixs >= drop ? ixs + 1 : ixs)...>{};
}

//Creates a monotonically increasing sequence on the range [0, `count`), except
//that `drop` will not appear.
template<size_t count, size_t drop>
constexpr auto calc_drop_copy_sequence()
{
    static_assert(count > 0, "You cannot pass an empty sequence.");
    static_assert(drop < count, "The drop index must be less than the count.");
    constexpr auto count_sequence = std::make_index_sequence<count - 1>();
    return calc_drop_sequence_dropper<drop>(count_sequence);
}

template<typename Tuple, size_t ...ixs>
constexpr auto copy_move_tuple_by_sequence(Tuple &&tpl, std::index_sequence<ixs...>)
{
    using TplType = std::remove_reference_t<Tuple>;

    return std::tuple<std::tuple_element_t<ixs, TplType>...>(
        std::get<ixs>(std::forward<Tuple>(tpl))...);
}

template<size_t drop, typename Tuple>
constexpr auto drop_tuple_element(Tuple &&tpl)
{
    using TplType = std::remove_reference_t<Tuple>;

    constexpr size_t tpl_size = std::tuple_size<TplType>::value;

    constexpr auto copy_seq = calc_drop_copy_sequence<tpl_size, drop>();

    return copy_move_tuple_by_sequence(std::forward<Tuple>(tpl), copy_seq);
}

The principle function is drop_tuple_element, which does what your hypothetical drop function does. Of course, if you're dropping multiple elements, you want to drop them all at once, rather than individually. So you'll need to modify the code.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
1

std::tuple is immutable, you can't drop or remove.

std::tuple is a fixed-size collection of heterogeneous values

Depending on your C++ taste, create a new tuple with std::get(std::tuple) and std::make_tuple

auto tac = std::make_tuple(std::get<0>(tabc), std::get<2>(tabc));

Or with C++14

auto tac = std::make_tuple(std::get<A>(tabc), std::get<C>(tabc));
Tony Tannous
  • 14,154
  • 10
  • 50
  • 86
  • Thank you. But i am looking for a more generic way. I know a type/index to drop and do not know others. – kyb Jun 14 '20 at 06:50
0

If you know indices of objects A and C in the tuple then you can use the function std::get with the template parameter size_t. Otherwise you can use the function std::get with type template parameter.

Here is a demonstrative program

#include <iostream>
#include <tuple>

struct A
{
};

struct B
{
};

struct C
{
};

int main() 
{
    auto t1 = std::make_tuple( A(), B(), C() );
    auto t2 = std::make_tuple( std::get<A>( t1 ), std::get<C>( t1 ) );
    auto t3 = std::make_tuple( std::get<0>( t1 ), std::get<2>( t1 ) );

    return 0;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335