问题 是否可以使用相互引用的C ++模板?


问题

我有以下两个结构声明:

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

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

而且我想找到 X 和 Y 这样我在替换之后得到这样的东西:

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

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

但我想在没有硬编码的情况下这样做 Yin 和 Yang 彼此的定义,所以 X 会是这样的 Yin<Yang<Yin<Yang<...>>>>

我可以在没有模板参数的情况下这样做:

struct Yin;
struct Yang;

struct Yin {
  Yang *m_ptr;
};

struct Yang {
  Yin *m_ptr;
};

但我的真实用例要复杂得多,我真的很想让它变得通用。有谁知道这样做的方法?或者可能会看到一些我不知道的明显事物?

我把这个问题标记为 c++14 因为我正在用clang编译相关的代码 -std=c++1y 我很高兴使用任何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;
}

13125
2017-08-27 23:13


起源

考虑到了正确的 X 和 Y会有无限的水平,我不知道如果没有明确的专业化,这怎么可能。 - T.C.
几乎使用额外的继承级别: coliru.stacked-crooked.com/a/4e5d8300be418a6e - dyp
@dyp好看!坚强是聪明的。编辑:呃..我误解了。它不太有用。虽然它很接近! - scpayson
我无法让它实际使用类型,只有这种额外的继承级别。但是,我可以制作类型 foo 和 bar 兼容: coliru.stacked-crooked.com/a/c6c3c0a6a6391c56 - dyp
我懂了: coliru.stacked-crooked.com/a/bcd9aa67b40df386这是正确的想法吗?可能想添加一些SFINAE,所以它不会意外地发现误报,但我认为这很好...... - Mooing Duck


答案:


专攻阴阳时 T 是模板类型和 void 是模板参数,它导致 Yin<Yang<void>> 指着一个 Yang<Yin<void>> 反之亦然,但没有任何明确的参考,所以你可以拥有任意数量的这些类型。只有一个专业化。

//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<void> 类型,所以我们需要应用具有类型特征的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<T>,其中包含需要专业化的成员。简单。


11
2017-08-27 23:26



“但是我想这样做,而不是将阴阳编成彼此的定义” - Lightness Races in Orbit
@LightnessRacesinOrbit:我不确定这是否适用于模板专业化,我们必须看看OP说的是什么。我想不出任何其他方式。 - Mooing Duck
在我的真实代码中,有几个类“行为像阴影”,有几个行为“像杨”,我希望能够将它们中的任何一个组合起来。因此,这种方法需要很多专业化。 - scpayson
@LightnessRacesinOrbit:处理投诉 - Mooing Duck
@scpayson:它现在扩展为N种类型 - Mooing Duck


是。首先,教 Yin 和 Yang 如何接受它们存储的类型的任意映射:

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”。

然后编写一个我们可以传递给的元模板类型函数 Yin 和 Yang

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>;
  };
};

这需要两件事 Yin 和 Yang 并且每次为第二个参数切换哪一个。

并测试它:

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

我们创建类型,和 它汇编

尝试去 清理一些语法失败 至少在那个版本的gcc中。

请注意无穷大的类型 - void 以上 - 可以更改,并且会产生不同类型。

在这种情况下,有 没有 连接之间 Yin 和 Yang除了映射之外,不需要对这两种类型进行任何操作。唯一的联系是在 flopper 元模板类型函数。映射类型函数甚至是相对不可知的 Yin 和 Yang  - 我用他们的名字,但没有。


0
2017-09-02 14:11