Given a tuple with vectors holding different types how do I extract the vector with minimum size?
You can't You can't directly.
Because they are different types, the decision is based on values (not on types), so you can't decide the type extracted compile time (std::tuple
can't be constexpr
), and C++ is a strong typed language.
The best simplest thing you can do is extract the index of the vector with minimum size. Because in this case the value extracted is an integer (std::size_t
, by example) and you can iterate the over the vectors in the tuple to select the one with less elements.
Different is if you have to extract the std::array
with minimum size
auto t = std::make_tuple(std::array<int, 2u>({1,2}),
std::array<double, 3u>({1.0, 2.0, 3.0}));
because the size (2u
for the first, 3u
for the second) is know compile time so you can select the second array compile time.
If you can use C++17, you can use std::variant
, so a type that can contain all types of your tuple.
As pointed by Caleth (thanks) if you can use only C++11/C++14 with boost libraries, you can use boost::variant
. But I don't know it so I can't show you a specific example (but you can see the Caleth's answer)
The following is an example with a two-types tuple
template <typename T1, typename T2>
std::variant<T1, T2> getLess (std::tuple<T1, T2> const & tp)
{
std::variant<T1, T2> v;
if ( std::get<0>(tp).size() < std::get<1>(tp).size() )
v = std::get<0>(tp);
else
v = std::get<1>(tp);
return v;
}
int main ()
{
std::vector<int> vi {1, 2, 3};
std::vector<double> vd {1.0, 2.0};
auto gl = getLess(std::make_tuple(vi, vd));
}
This works with a two types tuple. But with a N-types tuple (with N
high) become complicated because you can't write something as
auto min_length = std::get<0>(tp).size();
auto min_index = 0u;
for ( auto ui = 1u ; ui < N ; ++ui )
if ( std::get<ui>(tp).size() < min_length )
{
min_length = std::get<ui>(tp).size();
min_index = ui;
}
because you can't pass to std::get<>()
a run-time value as ui
.
Same problem assigning the variant. You can't simply write
v = std::get<min_index>(tp);
because min_index
is a run-time value.
You have to pass through a switch()
switch ( min_length )
{
case 0: v = std::get<0>(tp); break;
case 1: v = std::get<1>(tp); break;
case 2: v = std::get<2>(tp); break;
case 3: v = std::get<3>(tp); break;
case 4: v = std::get<4>(tp); break;
// ...
case N-1: v = std::get<N-1>(tp); break;
};
or something similar.
As you can see it's complicated and more complicated become if you want that the getLess()
function is a variadic one.
For the variadic case, the best I can imagine (but is a C++17 solution; see Caleth's answer for a C++11 solution) is to use an helper function and template folding as follows.
#include <tuple>
#include <vector>
#include <variant>
#include <iostream>
template <typename ... Ts, std::size_t ... Is>
auto getLessHelper (std::tuple<Ts...> const & tp,
std::index_sequence<0, Is...> const &)
{
std::variant<Ts...> var_ret { std::get<0>(tp) };
std::size_t min_size { std::get<0>(tp).size() };
((std::get<Is>(tp).size() < min_size ? (var_ret = std::get<Is>(tp),
min_size = std::get<Is>(tp).size())
: 0u), ...);
return var_ret;
}
template <typename ... Ts>
auto getLess (std::tuple<Ts...> const & tp)
{ return getLessHelper(tp, std::index_sequence_for<Ts...>{}); }
int main ()
{
std::vector<int> vi {1, 2, 3};
std::vector<double> vd {1.0, 2.0};
std::vector<float> vf {1.0f};
auto gl = getLess(std::make_tuple(vi, vd, vf));
std::cout << std::visit([](auto const & v){ return v.size(); }, gl)
<< std::endl; // print 1, the size() of vf
}