我有以下两个结构声明:
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
,因为我正在使用-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;
}
答案 0 :(得分:11)
在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>
,其中包含需要专门化的成员。容易。
答案 1 :(得分:0)
是。首先,教导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;
};
默认为&#34;只需T
&#34;。
然后编写一个元模板类型函数,我们可以传递给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};
我们创建了类型,it compiles。
至少在该版本的gcc中尝试clean up some of the syntax fail。
请注意,无穷大的类型 - void
以上 - 可以更改,并且会产生不同的类型。
在这种情况下,Yin
和Yang
之间存在无连接,除了该映射之外,不需要对这两种类型进行任何操作。唯一的连接是flopper
元模板类型函数。映射类型函数甚至对Yin
和Yang
都是相对不可知的 - 我使用了它们的名字,但没有必要。