【问题标题】:Is it possible to have mutually referential C++ templates?是否可以有相互引用的 C++ 模板?
【发布时间】:2014-08-27 23:13:10
【问题描述】:

问题

我有以下两个结构声明:

template <typename T>
struct Yin {
  T *m_ptr;
};

template <typename T>
struct Yang {
  T *m_ptr;
};

我想找到 XY 以便在替换后得到类似的东西:

// Not real C++ syntax
struct Yin<X> {
  Yang<Y> *m_ptr;
}

struct Yang<Y> {
  Yin<X> *m_ptr;
};

但我不想将YinYang 硬编码到彼此的定义中,所以X 会类似于Yin&lt;Yang&lt;Yin&lt;Yang&lt;...&gt;&gt;&gt;&gt;

我可以在没有模板参数的情况下做到这一点,例如:

struct Yin;
struct Yang;

struct Yin {
  Yang *m_ptr;
};

struct Yang {
  Yin *m_ptr;
};

但我的实际用例要复杂得多,我真的很想把它变成通用的。有谁知道实现这一目标的方法?或者也许会看到我遗漏的明显东西?

我已将此问题标记为c++14,因为我正在使用带有-std=c++1y 的clang 编译相关代码,并且我很高兴使用任何c++11/c++14 功能来完成这项工作。

一个无法编译的潜在解决方案。

这是一个看起来应该可以工作但无法编译的解决方案(并且给了我无用的错误消息):

template <typename T>
struct Yin {
  T *m_ptr;
};

template <typename T>
struct Yang {
  T *m_ptr;
};

template <template <class> class A, template <class> class B>
struct knot {
  using type = A<typename knot<B, A>::type>;
};

template <template <class> class A, template <class> class B>
using Tie = typename knot<A, B>::type;

int main() {
  // foo.cc:13:39: error: no type named 'type' in 'knot<Yin, Yang>'
  //  using type = A<typename knot<B, A>::type>;
  //                 ~~~~~~~~~~~~~~~~~~~~~^~~~
  Tie<Yin, Yang> x;
}

【问题讨论】:

标签: c++ templates c++14


【解决方案1】:

是的

T 是模板类型并且void 是模板参数时,专门化阴阳,这会导致Yin&lt;Yang&lt;void&gt;&gt; 指向Yang&lt;Yin&lt;void&gt;&gt;,反之亦然,但没有任何显式引用另一个,因此,您可以根据需要拥有任意数量的这些类型。只有一个专业。

//special recursive case
template <template<class> class other>
struct Yin<other<void>> 
{
    other<Yin<void>> *m_ptr;
};

template <template<class> class other>
struct Yang<other<void>> 
{
    other<Yang<void>> *m_ptr;
};

但是,这些特化适用于任何 template&lt;void&gt; 类型,因此我们需要应用具有类型特征的 SFINAE:

template<template<class> class T> struct is_yinyang : public std::false_type {};
template<> struct is_yinyang<Yin> : public std::true_type {}; 
template<> struct is_yinyang<Yang> : public std::true_type {} 

然后是这个可怕的部分,它非常复杂和丑陋,并且需要在阴/阳类型上添加一个毫无意义的额外模板参数:

//here's Yin + Specialization
template <typename T, class allowed=void>
struct Yin {
    T *m_ptr;
};
template<> struct is_yinyang<Yin> : public std::true_type {};

template <template<class,class> class other>
struct Yin<other<void,void>,typename std::enable_if<is_yinyang<other>::value>::type>
{
    other<Yin<void,void>,void> *m_ptr;
};

现在阴阳只引用自己,添加新的递归指针类型是微不足道的。编译证明在这里:http://coliru.stacked-crooked.com/a/47ecd31e7d48f617

“但是等等!”你惊呼,那我必须复制我所有的成员!不是简单地将Yin 拆分为具有共享成员的类,并让它继承自Yin_specialmembers&lt;T&gt;,其中包含需要专门化的成员。很简单。

【讨论】:

  • “但我不想将阴阳硬编码到彼此的定义中”
  • @LightnessRacesinOrbit:我不确定这是否适用于模板专业化,我们必须看看 OP 说了什么。我想不出别的办法。
  • 在我的真实代码中,有几个“像阴”的类和几个“像阳”的类,我希望能够组合它们中的任何一个。因此,这种方法需要很多专业知识。
  • @LightnessRacesinOrbit:投诉已解决
  • @scpayson:现在扩展为 N 种
【解决方案2】:

是的。首先,教YinYang 如何接受他们存储的类型的任意映射:

template<class T>struct identity{using type=T;};

template <typename T, template<class>class Z=identity>
struct Yin {
  template<class U>using Z_t=typename Z<U>::type;
  Z_t<T> *m_ptr;
};

template <typename T, template<class>class Z=identity>
struct Yang {
  template<class U>using Z_t=typename Z<U>::type;
  Z_t<T> *m_ptr;
};

默认为“只需接受T”。

然后编写一个元模板类型函数,我们可以将它传递给YinYang

template<
  template<class, template<class>class>class Yin,
  template<class, template<class>class>class Yang
>
struct flopper {
  template<class T> struct flip {
    using type = Yang<T, flopper<Yang, Yin>::template flip>;
  };
};

它接受YinYang 之类的两件事,并切换每次将哪一个应用于第二个参数。

并对其进行测试:

using yin = Yin< void, flopper<Yin, Yang>::template flip >;
using yang = Yang< void, flopper<Yang, Yin>::template flip >;
yin a = {nullptr};
yang b = {&a};

我们创建类型,然后it compiles

至少在那个版本的 gcc 中尝试clean up some of the syntax fail

请注意,无穷大处的类型——上面的void——可以更改,并产生不同的类型。

在这种情况下,YinYang 之间没有没有连接,除了该映射之外,无需对有问题的两种类型进行任何操作。唯一的连接是在flopper 元模板类型函数中。映射类型函数甚至与 YinYang 相对不可知——我使用了它们的名称,但没有必要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多