递归生成结构引用的所有不同类型的元组
Recursively generate tuple of all distinct types referenced by a struct
我在编译时使用 using
s 和 std::tuple
s 指定类型图,如下所示:
#include<tuple>
struct C;
struct A
{
using ReferencedTypes = std::tuple<>;
};
struct B
{
using ReferencedTypes = std::tuple<C>;
};
struct C
{
using ReferencedTypes = std::tuple<B, A>;
};
struct D
{
using ReferencedTypes = std::tuple<A, B, C>;
};
struct E
{
using ReferencedTypes = std::tuple<C, B>;
};
我想要的是一种递归地获取由这些类型中的任何一种(包括顶级类型)引用的所有不同类型的元组的方法。元组元素的顺序无关紧要。类似于:
// DistinctTypes is a std::tuple<E, C, B, A>;
using DistinctTypes = all_types_referenced_in_graph<E>::value;
我的模板元编程技能生疏了,但我有一些可以正确忽略循环的东西。
例如,我确信更好的答案不会强制使用 std::tuple_cat
和 SFINAE。
为了可读性,我用requires
代替了SFINAE黑魔法
首先,让我们编写一个元函数,它将在元组中进行线性搜索,以查找元素是否存在。如果存在,则 return 为真。否则,return 错误:
// Base case
template<typename Elem, typename Tuple>
struct tuple_contains : std::false_type {};
// Iteration. We extend the type until result. Looks like recursion.
// We drop the first element of the tuple for the next iteration, until none.
template<typename T, typename First, typename... Rest>
struct tuple_contains<T, std::tuple<First, Rest...>> : tuple_contains<T, std::tuple<Rest...>> {};
// Success! T is the same as the first element of the tuple!
template<typename T, typename... Rest>
struct tuple_contains<T, std::tuple<T, Rest...>> : std::true_type {};
使用它,我们可以创建一个 class,它将递归地进入元组,并且仅当该元素不在列表中时才将类型附加到列表中:
// We will only use specializations
template<typename TupIn, typename TupOut = std::tuple<>>
struct recursive_append_unique;
// End case. No more types to process.
template<typename... TsOut>
struct recursive_append_unique<std::tuple<>, std::tuple<TsOut...>> {
using type = std::tuple<TsOut...>;
};
// Here we receive std::tuple<T, TsIn...> where T is already in TsOut.
// In that case, we skip it since it's already processed
template<typename T, typename... TsIn, typename... TsOut>
requires (tuple_contains<T, std::tuple<TsOut...>>::value)
struct recursive_append_unique<std::tuple<T, TsIn...>, std::tuple<Ts...>> {
using type = recursive_append_unique<std::tuple<TsIn...>, std::tuple<TsOut...>>::type;
};
// Here we have a T that is not in TsOut.
// Here's the core of the algorithm: We add T into TsOut,
// But we also add all types on T::ReferencedTypes into TsIn.
// The next iteration will take care of the first type we added into TsIn.
template<typename T, typename... TsIn, typename... TsOut>
struct recursive_append_unique<std::tuple<T, TsIn...>, std::tuple<TsOut...>> {
using type = recursive_append_unique<decltype(std::tuple_cat(std::declval<typename T::ReferencedTypes>(), std::declval<std::tuple<TsIn...>>())), std::tuple<TsOut..., T>>::type;
};
下面是它在您的代码中的用法:
int main() {
static_assert(std::same_as<recursive_append_unique<std::tuple<E>>::type, std::tuple<E, C, B, A>>);
}
我相信这可以更简单地完成,但是我已经有一段时间没有使用模板元程序了。
requires 子句可以替换为默认为 void
的虚拟第三个模板参数,并结合 std::enable_if_t
.
我在编译时使用 using
s 和 std::tuple
s 指定类型图,如下所示:
#include<tuple>
struct C;
struct A
{
using ReferencedTypes = std::tuple<>;
};
struct B
{
using ReferencedTypes = std::tuple<C>;
};
struct C
{
using ReferencedTypes = std::tuple<B, A>;
};
struct D
{
using ReferencedTypes = std::tuple<A, B, C>;
};
struct E
{
using ReferencedTypes = std::tuple<C, B>;
};
我想要的是一种递归地获取由这些类型中的任何一种(包括顶级类型)引用的所有不同类型的元组的方法。元组元素的顺序无关紧要。类似于:
// DistinctTypes is a std::tuple<E, C, B, A>;
using DistinctTypes = all_types_referenced_in_graph<E>::value;
我的模板元编程技能生疏了,但我有一些可以正确忽略循环的东西。
例如,我确信更好的答案不会强制使用 std::tuple_cat
和 SFINAE。
为了可读性,我用requires
代替了SFINAE黑魔法
首先,让我们编写一个元函数,它将在元组中进行线性搜索,以查找元素是否存在。如果存在,则 return 为真。否则,return 错误:
// Base case
template<typename Elem, typename Tuple>
struct tuple_contains : std::false_type {};
// Iteration. We extend the type until result. Looks like recursion.
// We drop the first element of the tuple for the next iteration, until none.
template<typename T, typename First, typename... Rest>
struct tuple_contains<T, std::tuple<First, Rest...>> : tuple_contains<T, std::tuple<Rest...>> {};
// Success! T is the same as the first element of the tuple!
template<typename T, typename... Rest>
struct tuple_contains<T, std::tuple<T, Rest...>> : std::true_type {};
使用它,我们可以创建一个 class,它将递归地进入元组,并且仅当该元素不在列表中时才将类型附加到列表中:
// We will only use specializations
template<typename TupIn, typename TupOut = std::tuple<>>
struct recursive_append_unique;
// End case. No more types to process.
template<typename... TsOut>
struct recursive_append_unique<std::tuple<>, std::tuple<TsOut...>> {
using type = std::tuple<TsOut...>;
};
// Here we receive std::tuple<T, TsIn...> where T is already in TsOut.
// In that case, we skip it since it's already processed
template<typename T, typename... TsIn, typename... TsOut>
requires (tuple_contains<T, std::tuple<TsOut...>>::value)
struct recursive_append_unique<std::tuple<T, TsIn...>, std::tuple<Ts...>> {
using type = recursive_append_unique<std::tuple<TsIn...>, std::tuple<TsOut...>>::type;
};
// Here we have a T that is not in TsOut.
// Here's the core of the algorithm: We add T into TsOut,
// But we also add all types on T::ReferencedTypes into TsIn.
// The next iteration will take care of the first type we added into TsIn.
template<typename T, typename... TsIn, typename... TsOut>
struct recursive_append_unique<std::tuple<T, TsIn...>, std::tuple<TsOut...>> {
using type = recursive_append_unique<decltype(std::tuple_cat(std::declval<typename T::ReferencedTypes>(), std::declval<std::tuple<TsIn...>>())), std::tuple<TsOut..., T>>::type;
};
下面是它在您的代码中的用法:
int main() {
static_assert(std::same_as<recursive_append_unique<std::tuple<E>>::type, std::tuple<E, C, B, A>>);
}
我相信这可以更简单地完成,但是我已经有一段时间没有使用模板元程序了。
requires 子句可以替换为默认为 void
的虚拟第三个模板参数,并结合 std::enable_if_t
.