如何从元组中删除第 n 个元素?
How to remove the nth element from a tuple?
我正在尝试编写一个函数来从现有的 std::tuple
中创建一个新的 std::tuple
,并跳过给定索引上的元素。例如:
我有一个元组 t
定义如下:
constexpr auto t = std::tuple(1, 2, 3, 4);
我想把它复制到另一个元组中。但是,我想跳过 nth 元素。假设在这种情况下,我想跳过的 nth 元素是 3(这意味着我想跳过索引为 2 的元素)。这将导致一个新的元组定义为:
std::tuple(1, 2, 4);
这是我到现在为止最接近的:
template<std::size_t N, typename T, std::size_t ... is>
constexpr auto fun(T&& tp, std::index_sequence<is...>&& i) noexcept {
return std::tuple((is != N ? std::get<is>(tp) : 0) ...);
}
template<std::size_t N, std::size_t... elems>
constexpr auto fun2() noexcept {
constexpr auto t = std::tuple(elems...);
return fun<N>(std::forward_as_tuple(elems...), std::make_index_sequence<sizeof...(elems)>());
}
但是,我没有删除第 nth 元素,而是将其设置为 0。
理想情况下,我会更改函数 fun()
中的 return 参数以使用多个临时元组创建一个新元组:
return std::tuple_cat((is != N ? std::tuple(std::get<is>(tp)) : std::tuple()) ...);
然而,问题是三元运算符必须在两边都有匹配的类型。
我尝试的另一种方法是基于递归:
template<std::size_t N, std::size_t head, std::size_t... tail>
constexpr auto fun3() noexcept {
if constexpr(!sizeof...(tail))
return std::tuple(head);
if constexpr(sizeof...(tail) - 1 == N)
return std::tuple_cat(fun3<N, tail...>());
if constexpr(sizeof...(tail) - 1 != N)
return std::tuple_cat(std::tuple(head), fun3<N, tail...>());
}
然而,那更不成功。在这种情况下,如果 N
等于 0,则 nth 元素(这里也是第一个元素)仍将在新元组中使用。此外,这甚至无法编译,因为第二条语句存在问题:
if constexpr(sizeof...(tail) - 1 == N)
我在这里错过了什么?如何复制元组并在复制过程中跳过其元素之一?
我正在使用 C++17,我需要在编译时计算函数。
那
呢
return std::tuple_cat( foo<is, N>::func(std::get<is>(tp)) ...);
其中 foo
是具有如下特化的结构?
template <std::size_t, std::size_t>
struct foo
{
template <typename T>
static auto func (T const & t)
{ return std::make_tuple(t); }
}
template <std::size_t N>
struct foo<N, N>
{
template <typename T>
static std::tuple<> func (T const &)
{ return {}; }
}
(注意:代码未经测试)。
这几乎就是你的三元运算符的想法,但没有匹配双方类型的问题:只有正确的类型被实例化。
发布问题几分钟后,我找到了解决方法。这并不理想,但是嘿:
template<std::size_t N, typename T, std::size_t ... is>
constexpr auto fun(T&& tp, std::index_sequence<is...>&& i) noexcept {
return std::tuple((is < N ? std::get<is>(tp) : std::get<is+1>(tp)) ...);
}
template<std::size_t N, std::size_t... elems>
constexpr auto fun2() noexcept {
constexpr auto t = std::tuple(elems...);
return fun<N>(std::forward_as_tuple(elems...), std::make_index_sequence<sizeof... (elems) - 1>());
}
这样,我们复制了第 nth 元素之前的所有元素,当我们到达 nth 元素时,我们增加每个下一个索引为 1。我们不会超出范围,因为我们传递的 index_sequence 比传递的元组少 1 个元素。
我希望这个回答对某人有所帮助。
另一个解决方案是创建两个索引序列,分别引用元组的前后部分。
template<std::size_t nth, std::size_t... Head, std::size_t... Tail, typename... Types>
constexpr auto remove_nth_element_impl(std::index_sequence<Head...>, std::index_sequence<Tail...>, std::tuple<Types...> const& tup) {
return std::tuple{
std::get<Head>(tup)...,
// We +1 to refer one element after the one removed
std::get<Tail + nth + 1>(tup)...
};
}
template<std::size_t nth, typename... Types>
constexpr auto remove_nth_element(std::tuple<Types...> const& tup) {
return remove_nth_element_impl<nth>(
std::make_index_sequence<nth>(), // We -1 to drop one element
std::make_index_sequence<sizeof...(Types) - nth - 1>(),
tup
);
}
下面是这个函数的测试:
int main() {
constexpr auto tup = std::tuple{1, 1.2, 'c'};
constexpr auto tup2 = remove_nth_element<0>(tup);
constexpr auto tup3 = remove_nth_element<2>(tup);
static_assert(std::is_same_v<decltype(tup2), const std::tuple<double, char>>);
static_assert(std::is_same_v<decltype(tup3), const std::tuple<int, double>>);
return 0;
}
此解决方案的优点是不构建中间元组且不使用 std::tuple_cat
,这两者在编译时都可能很困难。
我正在尝试编写一个函数来从现有的 std::tuple
中创建一个新的 std::tuple
,并跳过给定索引上的元素。例如:
我有一个元组 t
定义如下:
constexpr auto t = std::tuple(1, 2, 3, 4);
我想把它复制到另一个元组中。但是,我想跳过 nth 元素。假设在这种情况下,我想跳过的 nth 元素是 3(这意味着我想跳过索引为 2 的元素)。这将导致一个新的元组定义为:
std::tuple(1, 2, 4);
这是我到现在为止最接近的:
template<std::size_t N, typename T, std::size_t ... is>
constexpr auto fun(T&& tp, std::index_sequence<is...>&& i) noexcept {
return std::tuple((is != N ? std::get<is>(tp) : 0) ...);
}
template<std::size_t N, std::size_t... elems>
constexpr auto fun2() noexcept {
constexpr auto t = std::tuple(elems...);
return fun<N>(std::forward_as_tuple(elems...), std::make_index_sequence<sizeof...(elems)>());
}
但是,我没有删除第 nth 元素,而是将其设置为 0。
理想情况下,我会更改函数 fun()
中的 return 参数以使用多个临时元组创建一个新元组:
return std::tuple_cat((is != N ? std::tuple(std::get<is>(tp)) : std::tuple()) ...);
然而,问题是三元运算符必须在两边都有匹配的类型。
我尝试的另一种方法是基于递归:
template<std::size_t N, std::size_t head, std::size_t... tail>
constexpr auto fun3() noexcept {
if constexpr(!sizeof...(tail))
return std::tuple(head);
if constexpr(sizeof...(tail) - 1 == N)
return std::tuple_cat(fun3<N, tail...>());
if constexpr(sizeof...(tail) - 1 != N)
return std::tuple_cat(std::tuple(head), fun3<N, tail...>());
}
然而,那更不成功。在这种情况下,如果 N
等于 0,则 nth 元素(这里也是第一个元素)仍将在新元组中使用。此外,这甚至无法编译,因为第二条语句存在问题:
if constexpr(sizeof...(tail) - 1 == N)
我在这里错过了什么?如何复制元组并在复制过程中跳过其元素之一?
我正在使用 C++17,我需要在编译时计算函数。
那
呢return std::tuple_cat( foo<is, N>::func(std::get<is>(tp)) ...);
其中 foo
是具有如下特化的结构?
template <std::size_t, std::size_t>
struct foo
{
template <typename T>
static auto func (T const & t)
{ return std::make_tuple(t); }
}
template <std::size_t N>
struct foo<N, N>
{
template <typename T>
static std::tuple<> func (T const &)
{ return {}; }
}
(注意:代码未经测试)。
这几乎就是你的三元运算符的想法,但没有匹配双方类型的问题:只有正确的类型被实例化。
发布问题几分钟后,我找到了解决方法。这并不理想,但是嘿:
template<std::size_t N, typename T, std::size_t ... is>
constexpr auto fun(T&& tp, std::index_sequence<is...>&& i) noexcept {
return std::tuple((is < N ? std::get<is>(tp) : std::get<is+1>(tp)) ...);
}
template<std::size_t N, std::size_t... elems>
constexpr auto fun2() noexcept {
constexpr auto t = std::tuple(elems...);
return fun<N>(std::forward_as_tuple(elems...), std::make_index_sequence<sizeof... (elems) - 1>());
}
这样,我们复制了第 nth 元素之前的所有元素,当我们到达 nth 元素时,我们增加每个下一个索引为 1。我们不会超出范围,因为我们传递的 index_sequence 比传递的元组少 1 个元素。
我希望这个回答对某人有所帮助。
另一个解决方案是创建两个索引序列,分别引用元组的前后部分。
template<std::size_t nth, std::size_t... Head, std::size_t... Tail, typename... Types>
constexpr auto remove_nth_element_impl(std::index_sequence<Head...>, std::index_sequence<Tail...>, std::tuple<Types...> const& tup) {
return std::tuple{
std::get<Head>(tup)...,
// We +1 to refer one element after the one removed
std::get<Tail + nth + 1>(tup)...
};
}
template<std::size_t nth, typename... Types>
constexpr auto remove_nth_element(std::tuple<Types...> const& tup) {
return remove_nth_element_impl<nth>(
std::make_index_sequence<nth>(), // We -1 to drop one element
std::make_index_sequence<sizeof...(Types) - nth - 1>(),
tup
);
}
下面是这个函数的测试:
int main() {
constexpr auto tup = std::tuple{1, 1.2, 'c'};
constexpr auto tup2 = remove_nth_element<0>(tup);
constexpr auto tup3 = remove_nth_element<2>(tup);
static_assert(std::is_same_v<decltype(tup2), const std::tuple<double, char>>);
static_assert(std::is_same_v<decltype(tup3), const std::tuple<int, double>>);
return 0;
}
此解决方案的优点是不构建中间元组且不使用 std::tuple_cat
,这两者在编译时都可能很困难。