简体   繁体   English

可以在 C++ 中实例化和/或专门化递归/自引用模板(使用指针)吗?

[英]Can a recursive/self-referential template (using pointers) be instantiated and/or specialized in C++?

I want to instantiate a template from the STL, using maps,vectors, and arrays, as follows:我想从 STL 实例化一个模板,使用地图、矢量和 arrays,如下所示:

map<some_type,vector<map<some_type,vector...>*>> elements; map<some_type,vector<map<some_type,vector...>*>> 元素;

The ellipses is just pseudo-code to represent the infinitely recursive definition, which is ofcourse impossible to type out.省略号只是表示无限递归定义的伪代码,当然不可能打出来。 Basically, the vector should just hold pointers to other maps that are identical in structure/definition to the map in which the vector is contained.基本上,向量应该只包含指向其他映射的指针,这些映射在结构/定义上与包含向量的 map 相同。 I know there are workarounds using classes and structs, the question is whether it is possible using only templates.我知道有使用类和结构的解决方法,问题是是否可以只使用模板。 I was hoping I could somehow define the whole outer map as some kind of "template-variable" or other place-holder such as "T", then write the following:我希望我能以某种方式将整个外部 map 定义为某种“模板变量”或其他占位符,例如“T”,然后编写以下内容:

map<some_type,vector<T*>> elements; map<some_type,vector<T*>> 元素;

where I would separately define T as referring to the whole map. But due to recursion, such a variable T would be defined in terms of itself, ie sub-components that are themselves T. Later I would then at runtime as necessary allocate more maps on the heap and insert pointers to them in the vector, such that I can then recursively (indefinately often), traverse into the map within the vector, just so that I can then instantiate more maps on the heap, again holding pointers to them within the vector.我将单独定义 T 为引用整个 map。但是由于递归,这样的变量 T 将根据自身定义,即本身为 T 的子组件。稍后我会在运行时根据需要分配更多映射在堆上并将指向它们的指针插入向量中,这样我就可以递归地(不确定地经常)遍历向量中的 map,这样我就可以在堆上实例化更多映射,再次将指向它们的指针保存在其中向量。

Is there an (elegant) way to do this (if at all)?有没有一种(优雅的)方法来做到这一点(如果有的话)?

You were on the right track by abstracting out the recursion variable:通过抽象出递归变量,您走在了正确的轨道上:

template <typename Self>
using F = std::map<int, std::vector<Self*>>;

The problem is to find a type T such that T == F<T> .问题是找到满足T == F<T>的类型T This is known as finding the fixed point .这被称为寻找固定点 In these terms, we want a template Fix taking a template template parameter such that Fix<F> == F<Fix<F>> .在这些术语中,我们想要一个模板Fix采用模板模板参数,例如Fix<F> == F<Fix<F>>

Abstractly, in a lazy functional language, Fix<F> = F<Fix<F>> could serve as a definition of Fix<F> .抽象地,在惰性函数式语言中, Fix<F> = F<Fix<F>>可以作为Fix<F>的定义。 This coincidentally tells us exactly what breaks down in C++. In C++ notation this hypothetical definition would look like:这巧合地告诉我们 C++ 中发生故障的确切原因。在 C++ 符号中,这个假设定义如下所示:

template <template<typename> typename F>
using Fix = F<Fix<F>>; // does not compile

This depends fundamentally on laziness, but templates are lazy by nature so that isn't a problem.这从根本上取决于惰性,但模板本质上是惰性的,所以这不是问题。 The real problem is name lookup.真正的问题是名称查找。 We cannot refer to Fix on the right-hand side in C++. That's a somewhat artificial restriction, but that's the language we have.我们不能在 C++ 的右侧引用Fix 。这是一个有点人为的限制,但这是我们的语言。

I cannot see a way around that, so I cannot avoid introducing one generic helper struct:我看不出解决这个问题的方法,所以我无法避免引入一个通用的辅助结构:

template <template<typename> typename F>
struct Fix : F<Fix<F>> { };

Although aliases cannot reference their own name in the definition, classes and structs can.虽然别名不能在定义中引用它们自己的名称,但类和结构可以。

With all of that out of the way, we have our solution:解决所有这些问题后,我们就有了解决方案:

// Our type
using Type = Fix<F>;

// It instantiates
auto map = Type{};

// The inner type is the same as the outer type
using inner_type = std::decay_t<decltype(*std::declval<Type::mapped_type::value_type>())>;
static_assert(std::is_same_v<Type, inner_type>);

// We can push_back the address of ourself
map[0].push_back(&map);

See this on godbolt.在 godbolt 上看到这个。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM