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

发布于 2025-01-10 22:37:31 字数 591 浏览 0 评论 0 原文

我想使用映射、向量和数组从 STL 实例化一个模板,如下所示:

地图*>>>元素;

省略号只是表示无限递归定义的伪代码,这当然是不可能打印出来的。基本上,向量应该只保存指向其他映射的指针,这些映射在结构/定义上与包含向量的映射相同。我知道有使用类和结构的解决方法,问题是是否可以仅使用模板。我希望我能以某种方式将整个外部地图定义为某种“模板变量”或其他占位符,例如“T”,然后编写以下内容:

地图>元素;

我将单独将 T 定义为指整个地图。但是由于递归,这样的变量 T 将根据其自身定义,即本身就是 T 的子组件。稍后,我将在运行时根据需要在堆上分配更多映射,并在向量中插入指向它们的指针,例如然后我可以递归地(无限频繁地)遍历向量内的映射,这样我就可以在堆上实例化更多映射,再次在向量内保存指向它们的指针。

有没有一种(优雅的)方法来做到这一点(如果有的话)?

I want to instantiate a template from the STL, using maps,vectors, and arrays, as follows:

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

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. 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<some_type,vector<T*>> elements;

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.

Is there an (elegant) way to do this (if at all)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

污味仙女 2025-01-17 22:37:31

通过抽象出递归变量,您走上了正确的道路:

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

问题是找到一个类型 T 使得 T == F。这称为查找固定点。在这些方面,我们需要一个模板 Fix 接受模板模板参数,使得 Fix == F<修复>

抽象地,在惰性函数式语言中,Fix = F> 可以用作 Fix 的定义。这恰好告诉我们 C++ 中到底发生了什么问题。在 C++ 表示法中,这个假设的定义如下所示:

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

这从根本上取决于惰性,但模板本质上是惰性的,因此这不是问题。真正的问题是名称查找。我们不能引用C++中右侧的Fix。这是一个有点人为的限制,但这就是我们所拥有的语言。

我看不到解决这个问题的方法,所以我无法避免引入一个通用的辅助结构:

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

虽然别名不能在定义中引用它们自己的名称,但类和结构可以。

解决了所有这些问题后,我们有了解决方案:

// 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);

请在 godbolt 上查看此内容。

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>. 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>>.

Abstractly, in a lazy functional language, Fix<F> = F<Fix<F>> could serve as a definition of Fix<F>. This coincidentally tells us exactly what breaks down in C++. In C++ notation this hypothetical definition would look like:

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文