给定一个元组,其中包含任意数量的包含不同类型的向量,我如何提取最小大小的向量?
Given a tuple with arbitrary number of vectors holding different types how do I extract the vector with minimum size?
在我的应用程序中,我生成了一个包含许多保存值的向量的元组。我想要一种遍历元组以提取具有最小值的单个向量的通用方法。
例如:
auto t = std::make_tuple(std::vector<int>({1,2}), std::vector<double>({1.0, 2.0, 3.0}));
如何提取包含最小值的向量?
Given a tuple with vectors holding different types how do I extract the vector with minimum size?
不能不能直接
因为它们是不同的类型,决定的是值(不是类型),所以你不能决定提取编译时的类型(std::tuple
不能constexpr
) , 而 C++ 是一种强类型语言。
您可以做的最好最简单的事情是提取具有最小大小的向量的索引。因为在这种情况下,提取的值是一个整数(std::size_t
,例如),您可以将元组中的向量迭代到 select 元素较少的向量。
不同的是如果你必须提取最小尺寸std::array
auto t = std::make_tuple(std::array<int, 2u>({1,2}),
std::array<double, 3u>({1.0, 2.0, 3.0}));
因为大小(第一个2u
,第二个3u
)是编译时知道的,所以你可以select第二个数组编译时。
如果你可以使用 C++17,你可以使用 std::variant
,这样一个类型可以包含你的元组的所有类型。
正如 Caleth 所指出的(谢谢),如果您只能将 C++11/C++14 与 boost 库一起使用,则可以使用 boost::variant
。但是我不知道所以我不能给你一个具体的例子(但是你可以看到Caleth的回答)
下面是一个包含两种元组的例子
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));
}
这适用于两种类型的元组。但是对于 N 型元组(N
高)变得复杂,因为你不能写一些东西作为
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;
}
因为您无法将 std::get<>()
的 运行 时间值传递给 ui
。
分配变体时遇到同样的问题。你不能简单地写
v = std::get<min_index>(tp);
因为min_index
是一个运行时间值。
你必须通过一个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;
};
或类似的东西。
如您所见,如果您希望 getLess()
函数是一个可变函数,它会变得更复杂。
对于可变参数情况,我能想象的最好的(但是是 C++17 解决方案;请参阅 Caleth 对 C++11 解决方案的回答)是使用辅助函数和模板折叠,如下所示。
#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
}
假设我们的类型包中没有重复项,或者 a way of removing them, and also a C++11 backport of std::index_sequence
#include <map>
#include <tuple>
#include <functional>
#include <iostream>
#include <boost/variant>
#include <future/index_sequence>
namespace detail {
template<typename Variant, typename Tuple, std::size_t... Is>
std::map<std::size_t, std::function<Variant(const Tuple &)>> from_tuple_map(index_sequence<Is...>)
{
return { { Is, [](const Tuple & tup){ return std::get<Is>(tup); } }... };
}
struct GetSize
{
template<typename T>
std::size_t operator()(const std::vector<T> & vec){ return vec.size(); }
}; // becomes C++14 generic lambda
}
template<typename... Ts>
boost::variant<Ts...> from_tuple(const std::tuple<Ts...> & tup)
{
auto map = detail::from_tuple_map<boost::variant<Ts...>, std::tuple<Ts...>>(make_index_sequence<sizeof...(Ts)>());
auto get_size = GetSize{};
std::size_t best = 0;
std::size_t len = visit(get_size, map[best](tup));
for (std::size_t trial = 1; trial < sizeof...(Ts); ++trial)
{
std::size_t trial_len = visit(get_size, map[trial](tup));
if (trial_len > len)
{
best = trial;
len = trial_len;
}
}
return map[best](tup);
}
int main()
{
auto x = from_tuple(std::make_tuple(std::vector<int>({1,2}), std::vector<double>({1.0, 2.0, 3.0})));
visit([](const auto & a){ std::cout << a[1]; }, x);
}
在我的应用程序中,我生成了一个包含许多保存值的向量的元组。我想要一种遍历元组以提取具有最小值的单个向量的通用方法。
例如:
auto t = std::make_tuple(std::vector<int>({1,2}), std::vector<double>({1.0, 2.0, 3.0}));
如何提取包含最小值的向量?
Given a tuple with vectors holding different types how do I extract the vector with minimum size?
不能不能直接
因为它们是不同的类型,决定的是值(不是类型),所以你不能决定提取编译时的类型(std::tuple
不能constexpr
) , 而 C++ 是一种强类型语言。
您可以做的最好最简单的事情是提取具有最小大小的向量的索引。因为在这种情况下,提取的值是一个整数(std::size_t
,例如),您可以将元组中的向量迭代到 select 元素较少的向量。
不同的是如果你必须提取最小尺寸std::array
auto t = std::make_tuple(std::array<int, 2u>({1,2}),
std::array<double, 3u>({1.0, 2.0, 3.0}));
因为大小(第一个2u
,第二个3u
)是编译时知道的,所以你可以select第二个数组编译时。
如果你可以使用 C++17,你可以使用 std::variant
,这样一个类型可以包含你的元组的所有类型。
正如 Caleth 所指出的(谢谢),如果您只能将 C++11/C++14 与 boost 库一起使用,则可以使用 boost::variant
。但是我不知道所以我不能给你一个具体的例子(但是你可以看到Caleth的回答)
下面是一个包含两种元组的例子
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));
}
这适用于两种类型的元组。但是对于 N 型元组(N
高)变得复杂,因为你不能写一些东西作为
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;
}
因为您无法将 std::get<>()
的 运行 时间值传递给 ui
。
分配变体时遇到同样的问题。你不能简单地写
v = std::get<min_index>(tp);
因为min_index
是一个运行时间值。
你必须通过一个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;
};
或类似的东西。
如您所见,如果您希望 getLess()
函数是一个可变函数,它会变得更复杂。
对于可变参数情况,我能想象的最好的(但是是 C++17 解决方案;请参阅 Caleth 对 C++11 解决方案的回答)是使用辅助函数和模板折叠,如下所示。
#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
}
假设我们的类型包中没有重复项,或者 a way of removing them, and also a C++11 backport of std::index_sequence
#include <map>
#include <tuple>
#include <functional>
#include <iostream>
#include <boost/variant>
#include <future/index_sequence>
namespace detail {
template<typename Variant, typename Tuple, std::size_t... Is>
std::map<std::size_t, std::function<Variant(const Tuple &)>> from_tuple_map(index_sequence<Is...>)
{
return { { Is, [](const Tuple & tup){ return std::get<Is>(tup); } }... };
}
struct GetSize
{
template<typename T>
std::size_t operator()(const std::vector<T> & vec){ return vec.size(); }
}; // becomes C++14 generic lambda
}
template<typename... Ts>
boost::variant<Ts...> from_tuple(const std::tuple<Ts...> & tup)
{
auto map = detail::from_tuple_map<boost::variant<Ts...>, std::tuple<Ts...>>(make_index_sequence<sizeof...(Ts)>());
auto get_size = GetSize{};
std::size_t best = 0;
std::size_t len = visit(get_size, map[best](tup));
for (std::size_t trial = 1; trial < sizeof...(Ts); ++trial)
{
std::size_t trial_len = visit(get_size, map[trial](tup));
if (trial_len > len)
{
best = trial;
len = trial_len;
}
}
return map[best](tup);
}
int main()
{
auto x = from_tuple(std::make_tuple(std::vector<int>({1,2}), std::vector<double>({1.0, 2.0, 3.0})));
visit([](const auto & a){ std::cout << a[1]; }, x);
}