删除第一种类型的 std::元组
Removing the first type of a std::tuple
这似乎是一个非常简单的问题:如何删除std::tuple
中的第一个(第 n 个(类型?
例:
typedef std::tuple<int, short, double> tuple1;
typedef std::tuple<short, double> tuple2;
上述操作将tuple1
转换为tuple2
。可能吗?
您可以使用基于类模板的部分专用化的简单类型函数:
#include <type_traits>
#include <tuple>
using namespace std;
template<typename T>
struct remove_first_type
{
};
template<typename T, typename... Ts>
struct remove_first_type<tuple<T, Ts...>>
{
typedef tuple<Ts...> type;
};
int main()
{
typedef tuple<int, bool, double> my_tuple;
typedef remove_first_type<my_tuple>::type my_tuple_wo_first_type;
static_assert(
is_same<my_tuple_wo_first_type, tuple<bool, double>>::value,
"Error!"
);
}
此外,此解决方案可以很容易地推广到删除元组的第 i 种类型:
#include <type_traits>
#include <tuple>
using namespace std;
template<size_t I, typename T>
struct remove_ith_type
{
};
template<typename T, typename... Ts>
struct remove_ith_type<0, tuple<T, Ts...>>
{
typedef tuple<Ts...> type;
};
template<size_t I, typename T, typename... Ts>
struct remove_ith_type<I, tuple<T, Ts...>>
{
typedef decltype(
tuple_cat(
declval<tuple<T>>(),
declval<typename remove_ith_type<I - 1, tuple<Ts...>>::type>()
)
) type;
};
int main()
{
typedef tuple<int, bool, double> my_tuple;
typedef remove_ith_type<1, my_tuple>::type my_tuple_wo_2nd_type;
static_assert(
is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value,
"Error!"
);
}
我写了一个被 C++14 标准接受的建议,这使得任何"类元组"类型都很容易做到,即支持 tuple_size
和 tuple_element
API 的类型:
template<typename T, typename Seq>
struct tuple_cdr_impl;
template<typename T, std::size_t I0, std::size_t... I>
struct tuple_cdr_impl<T, std::index_sequence<I0, I...>>
{
using type = std::tuple<typename std::tuple_element<I, T>::type...>;
};
template<typename T>
struct tuple_cdr
: tuple_cdr_impl<T, std::make_index_sequence<std::tuple_size<T>::value>>
{ };
并且,只需几个函数即可将元组对象转换为新类型:
template<typename T, std::size_t I0, std::size_t... I>
typename tuple_cdr<typename std::remove_reference<T>::type>::type
cdr_impl(T&& t, std::index_sequence<I0, I...>)
{
return std::make_tuple(std::get<I>(t)...);
}
template<typename T>
typename tuple_cdr<typename std::remove_reference<T>::type>::type
cdr(T&& t)
{
return cdr_impl(std::forward<T>(t),
std::make_index_sequence<std::tuple_size<T>::value>{});
}
这将创建一个整数序列[0,1,2,...,N)
其中N
tuple_size<T>::value
,然后创建一个新的元组,其中包含用于I
make_tuple(get<I>(t)...)
[1,2,...,N)
测试它:
using tuple1 = std::tuple<int, short, double>;
using tuple2 = std::tuple<short, double>;
using transformed = decltype(cdr(std::declval<tuple1>()));
static_assert(std::is_same<transformed, tuple2>::value, "");
static_assert(std::is_same<tuple_cdr<tuple1>::type, tuple2>::value, "");
#include <iostream>
int main()
{
auto t = cdr(std::make_tuple(nullptr, "hello", "world"));
std::cout << std::get<0>(t) << ", " << std::get<1>(t) << 'n';
}
我对提案的参考实现是 https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.h
我想出了一个与@Andy提出的解决方案非常相似的解决方案,但它试图通过直接处理参数包(使用虚拟包装器(而不是在std::tuple
上来变得更加通用。这样,该操作也可以应用于其他可变参数模板,而不仅仅是元组:
#include <type_traits>
#include <tuple>
template <typename... Args> struct pack {};
template <template <typename...> class T, typename Pack>
struct unpack;
template <template <typename...> class T, typename... Args>
struct unpack<T, pack<Args...>>
{
typedef T<Args...> type;
};
template <typename T, typename Pack>
struct prepend;
template <typename T, typename... Args>
struct prepend<T, pack<Args...>>
{
typedef pack<T, Args...> type;
};
template <std::size_t N, typename... Args>
struct remove_nth_type;
template <std::size_t N, typename T, typename... Ts>
struct remove_nth_type<N, T, Ts...>
: prepend<T, typename remove_nth_type<N-1, Ts...>::type>
{};
template <typename T, typename... Ts>
struct remove_nth_type<0, T, Ts...>
{
typedef pack<Ts...> type;
};
template <typename T, int N>
struct remove_nth;
template <template <typename...> class T, int N, typename... Args>
struct remove_nth<T<Args...>, N>
{
typedef typename
unpack<
T, typename
remove_nth_type<N, Args...>::type
>::type type;
};
template <typename... Args>
struct my_variadic_template
{
};
int main()
{
typedef std::tuple<int, bool, double> my_tuple;
typedef remove_nth<my_tuple, 1>::type my_tuple_wo_2nd_type;
static_assert(
is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value,
"Error!"
);
typedef my_variadic_template<int, double> vt;
typedef remove_nth<vt, 0>::type vt_wo_1st_type;
static_assert(
is_same<vt_wo_1st_type, my_variadic_template<double>>::value,
"Error!"
);
}
pack
是一个帮助程序结构,其唯一用途是存储模板参数包。 然后可以使用unpack
将参数解压缩到任意类模板中(感谢@BenVoigt这个技巧(。 prepend
只是将类型附加到包中。
remove_nth_type
使用部分模板专用化从参数包中删除第 n 种类型,将结果存储到pack
中。最后,remove_nth
采用任意类模板的专用化,从其模板参数中删除第 n 个类型,并返回新的专用化。
除了疯狂的TMP之外,还有一种非常简单的方法使用C++17 STL函数std::apply
:
#include <string>
#include <tuple>
template <class T, class... Args>
auto tail(const std::tuple<T, Args...>& t)
{
return std::apply(
[](const T&, const Args&... args)
{
return std::make_tuple(args...);
}, t);
}
template <class T>
using tail_t = decltype(tail(T{}));
int main()
{
std::tuple<int, double, std::string> t{1, 2., "3"};
auto _2_3 = tail(t);
using tuple_t = tail_t<std::tuple<int, double, std::string>>;
static_assert(std::is_same_v<std::tuple<double, std::string>, tuple_t>);
}
演示。
针对此任务的过度设计的template
元编程。 它包括通过过滤器template
对tuple
类型进行任意重新排序/重复/删除的能力:
#include <utility>
#include <type_traits>
template<typename... Ts> struct pack {};
template<std::size_t index, typename Pack, typename=void> struct nth_type;
template<typename T0, typename... Ts>
struct nth_type<0, pack<T0, Ts...>, void> { typedef T0 type; };
template<std::size_t index, typename T0, typename... Ts>
struct nth_type<index, pack<T0, Ts...>, typename std::enable_if<(index>0)>::type>:
nth_type<index-1, pack<Ts...>>
{};
template<std::size_t... s> struct seq {};
template<std::size_t n, std::size_t... s>
struct make_seq:make_seq<n-1, n-1, s...> {};
template<std::size_t... s>
struct make_seq<0,s...> {
typedef seq<s...> type;
};
template<typename T, typename Pack> struct conc_pack { typedef pack<T> type; };
template<typename T, typename... Ts> struct conc_pack<T, pack<Ts...>> { typedef pack<T, Ts...> type; };
template<std::size_t n, typename Seq> struct append;
template<std::size_t n, std::size_t... s>
struct append<n, seq<s...>> {
typedef seq<n, s...> type;
};
template<typename S0, typename S1> struct conc;
template<std::size_t... s0, std::size_t... s1>
struct conc<seq<s0...>, seq<s1...>>
{
typedef seq<s0..., s1...> type;
};
template<typename T, typename=void> struct value_exists:std::false_type {};
template<typename T> struct value_exists<T,
typename std::enable_if< std::is_same<decltype(T::value),decltype(T::value)>::value >::type
>:std::true_type {};
template<typename T, typename=void> struct result_exists:std::false_type {};
template<typename T> struct result_exists<T,
typename std::enable_if< std::is_same<typename T::result,typename T::result>::value >::type
>:std::true_type {};
template<template<std::size_t>class filter, typename Seq, typename=void>
struct filter_seq { typedef seq<> type; };
template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<value_exists<filter<s0>>::value>::type>
: append< filter<s0>::value, typename filter_seq<filter, seq<s...>>::type >
{};
template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && result_exists<filter<s0>>::value>::type>
: conc< typename filter<s0>::result, typename filter_seq<filter, seq<s...>>::type >
{};
template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && !result_exists<filter<s0>>::value>::type>
: filter_seq<filter, seq<s...>>
{};
template<typename Seq, typename Pack>
struct remap_pack {
typedef pack<> type;
};
template<std::size_t s0, std::size_t... s, typename Pack>
struct remap_pack< seq<s0, s...>, Pack >
{
typedef typename conc_pack< typename nth_type<s0, Pack>::type, typename remap_pack< seq<s...>, Pack >::type >::type type;
};
template<typename Pack>
struct get_indexes { typedef seq<> type; };
template<typename... Ts>
struct get_indexes<pack<Ts...>> {
typedef typename make_seq< sizeof...(Ts) >::type type;
};
template<std::size_t n>
struct filter_zero_out { enum{ value = n }; };
template<>
struct filter_zero_out<0> {};
template<std::size_t n>
struct filter_zero_out_b { typedef seq<n> result; };
template<>
struct filter_zero_out_b<0> { typedef seq<> result; };
#include <iostream>
int main() {
typedef pack< int, double, char > pack1;
typedef pack< double, char > pack2;
typedef filter_seq< filter_zero_out, typename get_indexes<pack1>::type >::type reindex;
typedef filter_seq< filter_zero_out_b, typename get_indexes<pack1>::type >::type reindex_b;
typedef typename remap_pack< reindex, pack1 >::type pack2_clone;
typedef typename remap_pack< reindex_b, pack1 >::type pack2_clone_b;
std::cout << std::is_same< pack2, pack2_clone >::value << "n";
std::cout << std::is_same< pack2, pack2_clone_b >::value << "n";
}
这里我们有一个类型pack
,它包含任意类型列表。 请参阅@LucTouraille 关于如何在tuple
和pack
之间移动的简洁答案。
seq
保存一系列索引。 remap_pack
取一个seq
和一个pack
,并通过抓取原始pack
的第n个元素来构建一个生成的pack
。
filter_seq
取一个template<size_t>
函子和一个seq
,并使用函子来过滤seq
的元素。 函子可以返回类型 size_t
或类型为 seq<...>
的::result
或两者都不::value
返回,从而允许一对一或一对多函子。
其他一些辅助函数,如conc
、append
、conc_pack
、get_indexes
、make_seq
,nth_type
四舍五入。
我用 filter_zero_out
对其进行了测试,这是一个基于 ::value
的过滤器,可以删除 0,filter_zero_out_b
是一个基于 ::result
的过滤器,也可以删除 0。
- 从类型std::函数传递变量失败,尽管调用方期望的类型完全相同
- 错误 C2679:二进制"<<":未找到采用类型 'std::string_view' 的右侧操作数的运算符(或者没有可接受的转换)
- 错误:无法传递非平凡类型"std::string"的对象和更多错误
- 与'operator<<'不匹配(操作数类型'std::ostream {aka std::basic_ostream<char>}'
- std::函数指针错误:无法将 &A::a 转换为类型 std::function<>&&
- 包括<字符串>,使用不完整的类型"std::string"
- 与"operator<<"不匹配(操作数类型'std::ostream'
- 错误:类型"std::__1::basic_string<char>"不提供呼叫运算符
- 错误:请求从"Mstream"转换为非标量类型"std::string {aka std::basic_string<char>}"
- 错误:移动文件时'QString'为非标量类型"std::__cxx11::字符串"
- CLion 无法解析类型 std::unordered_map,即使它提示我包含标头并且编译工作
- 字符串C :libc abi.dylib:终止未被发现的类型std :: out_of_range:basic
- 返回类型 std::optional<std::variant<...>>
- 变量具有不完整的类型std :: String;string.h有很多错误
- 对类型 'A *' 的非常量左值引用不能绑定到不相关的类型 'std::shared_ptr<A>' 的值
- 来自类型为std::basic_string::const_iterator的非常量引用的无效初始化,并且<char>来自类型std::basic_string<char>::
- 类型"std::array<char,6>"和"char"不兼容
- 此错误的含义是什么:从 'char* [40] 转换为非标量类型 'std::string
- 使用 TypedEq() 匹配类型 std::vector<std::vector>
- 如何修复错误:请求从"void"转换为非标量类型"std::vector<int>"