奇怪的是相互重复的类定义

时间:2018-01-12 13:57:16

标签: c++ templates language-lawyer template-meta-programming

我希望两个类中的类型声明相互依赖。这是第一个用clang和gcc编译的例子:

template <class Sum>
struct A
{
    using X = char;                // (1)
    using Z = typename Sum::B::Y;  // (2)
};

template <class Sum>
struct B
{
    using Y = typename Sum::A::X;
};

struct AplusB
{
    using A = ::A<AplusB>;
    using B = ::B<AplusB>;
};

AplusB::A::Z z;

int main() {}
然而,有一个有趣的时刻。如果你交换行(1)和(2),那么它将无法编译并出现错误:

  

错误:没有名为&#39; X&#39;在&#39; A&#39;

这让我怀疑原始代码在C ++标准意义上是否真的有效,或者它恰好编译?

这是第二个例子,它也利用了模板实例化的顺序:

template <class Sum>
struct A
{
    using X = char;
    using P = typename Sum::B::Q;
};

template <class Sum>
struct B
{
    using Y = typename Sum::A::X;
    using Q = int;
};

struct AplusB
{
    using A = ::A<AplusB>;
    using B = ::B<AplusB>;
};

AplusB::A::X z; // (1)
AplusB::B::Q t; // (2)

int main() {}

如果你交换(1)和(2),它将无法编译错误:

  

错误:没有名为&#39; Q&#39;在&#39; B&#39;

所以问题是:类定义的标准实际上是否允许它们相互依赖?

1 个答案:

答案 0 :(得分:2)

正如另一个answer中所讨论的那样,CWG 287的解决方案只是实现所遵循的事实上的方法,强制要求在实际成员的“内联”PoI之前的实体在范围内。

因此,当交换行时,我们尝试访问尚未实例化的内容:

template <class Sum>
struct A
{
    using X = char;               // (#)
    using P = typename Sum::B::Q; // (2.3), (1.2)
};

template <class Sum>
struct B
{
    using Y = typename Sum::A::X; // (2.2), (1.3)
    using Q = int;                // (*)
};

struct AplusB
{
    using A = ::A<AplusB>; // (1.1)
    using B = ::B<AplusB>; // (2.1) 
};

AplusB::B::Q t; // (2)

AplusB::A::X z; // (1) (INDEPENDENT example)

序列(2),(2.1),(2.2)和(2.3)是实例化的顺序,在该声明之前有效的PoI。随着(2)首先声明,在(2.3),我们引用(*),这不在范围内。如果(1)首先出现,那么在(1.3)我们访问(#),这确实在范围内。 (在随后的(2)声明中,A的专业化已经完全实例化,因此没有进一步的细微之处。)

如果您在“递归”之前声明了所有“基本案例”别名,这与您的第一个代码段不同,那就是fine either way