C++ 递归生成结构引用的所有不同类型的元组

C++ 递归生成结构引用的所有不同类型的元组,c++,tuples,c++17,metaprogramming,C++,Tuples,C++17,Metaprogramming,我在编译时使用和std::tuple指定类型图,如下所示: #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&g

我在编译时使用
std::tuple指定类型图,如下所示:

#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>;
};
#包括
结构C;
结构A
{
使用ReferencedTypes=std::tuple;
};
结构B
{
使用ReferencedTypes=std::tuple;
};
结构C
{
使用ReferencedTypes=std::tuple;
};
结构D
{
使用ReferencedTypes=std::tuple;
};
结构E
{
使用ReferencedTypes=std::tuple;
};
我想要的是一种递归地获取这些类型中任何一种(包括顶级类型)引用的所有不同类型的元组的方法。元组元素的顺序并不重要。比如:

// DistinctTypes is a std::tuple<E, C, B, A>;
using DistinctTypes = all_types_referenced_in_graph<E>::value;
//DistinctTypes是一个std::tuple;
使用DistinctTypes=所有类型\在\图中引用\值;

我的模板元编程技能已经过时,但我有一些可以正确忽略循环的东西

例如,我确信更好的答案不会强制使用
std::tuple\u cat
和SFINAE

为了可读性,我使用了
requires
而不是SFINAE暗器

首先,让我们编写一个元函数,它将在元组中线性搜索以查找元素是否存在。如果存在,则返回true。否则,返回false:

// 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 {};
//基本情况
模板
结构元组包含:std::false\u类型{};
//迭代。我们扩展类型直到结果。看起来像是递归。
//我们在下一次迭代中删除元组的第一个元素,直到没有。
模板
结构元组包含:元组包含{};
//成功!T与元组的第一个元素相同!
模板
结构元组包含:std::true\u类型{};
使用它,我们可以创建一个递归进入元组的类,并且仅当该元素不在列表中时才将类型附加到列表中:

// 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;
};
//我们将只使用专门化
模板
结构递归\u追加\u唯一;
//结案。没有更多要处理的类型。
模板
结构递归\u追加\u唯一{
使用type=std::tuple;
};
//这里我们接收到std::tuple,其中T已经在TsOut中。
//在这种情况下,我们跳过它,因为它已经被处理过了
模板
需要(元组包含::值)
结构递归\u追加\u唯一{
使用type=recursive\u append\u unique::type;
};
//这里我们有一个不在TsOut中的T。
//这是算法的核心:我们将T添加到TsOut中,
//但我们也将T::ReferencedTypes上的所有类型添加到了TsIn中。
//下一次迭代将处理我们添加到TsIn中的第一个类型。
模板
结构递归\u追加\u唯一{
使用type=recursive\u append\u unique::type;
};
下面是它在代码中的用法:

int main() {
    static_assert(std::same_as<recursive_append_unique<std::tuple<E>>::type, std::tuple<E, C, B, A>>);
}
intmain(){
静态断言(标准::与相同);
}
我相信这可以用更简单的方式来完成,但我已经有相当长的一段时间没有使用模板元程序了


requires子句可以由默认为
void
的第三个伪模板参数替换,如果
std::enable\u\t

您尝试了什么吗?修复现有代码比从头开始编写容易。是的,但我的元编程知识非常基础,我真的不认为我写的任何东西都有帮助。我还没有成功地处理循环(C->B->C)。用于替换
tuple_contains
模板概念匹配_any=(std::same_as||…)作为旁白,如果满足requires子句,则使专门化更加专业化,因此不需要否定的专门化。我提出了另一个怪诞的想法:简化我的
tuple\u cat\u uniq