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

scp*_*son 11 c++ templates c++14

问题

我有以下两个结构声明:

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

template <typename T>
struct Yang {
  T *m_ptr;
};
Run Code Online (Sandbox Code Playgroud)

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

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

struct Yang<Y> {
  Yin<X> *m_ptr;
};
Run Code Online (Sandbox Code Playgroud)

但我想没有硬编码这样做Yin,并Yang为彼此的定义,所以X会是这样Yin<Yang<Yin<Yang<...>>>>.

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

struct Yin;
struct Yang;

struct Yin {
  Yang *m_ptr;
};

struct Yang {
  Yin *m_ptr;
};
Run Code Online (Sandbox Code Playgroud)

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

我已经将这个问题标记为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;
}
Run Code Online (Sandbox Code Playgroud)

Moo*_*uck 11

如果T是模板类型并且void是模板参数,则将Yin和Yang专门化,这将导致Yin<Yang<void>>指向a 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;
};
Run Code Online (Sandbox Code Playgroud)

但是,这些专业化适用于任何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 {} 
Run Code Online (Sandbox Code Playgroud)

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

//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;
};
Run Code Online (Sandbox Code Playgroud)

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

"可是等等!" 你惊叹,然后我必须复制我的所有成员!不完全简单地拆分Yin成一个具有共享成员的类,并从中继承Yin_specialmembers<T>,其中包含需要专门化的成员.简单.

  • 在我的真实代码中,有几个类似"阴影"而且有几个类似"像杨",我希望能够将它们中的任何一个组合起来.因此,这种方法需要很多专业化. (3认同)