奇怪地相互重复的类定义 [英] Curiously mutually recurring class definitions

查看:56
本文介绍了奇怪地相互重复的类定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望两个类中的类型声明相互依赖.这是第一个同时使用clang和gcc进行编译的示例:

I want type declarations in two classes to mutually depend on each other. Here is a first example that compiles both with clang and 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),则它将编译失败并显示错误:

There is an interesting moment, however. If you swap lines (1) and (2), then it will fail to compile with an error:

错误:"A"中没有名为"X"的类型

error: no type named 'X' in 'A'

这使我怀疑原始代码实际上在C ++标准的意义上是有效的,还是恰好在编译时有效?

That makes me question whether the original code is actually valid in the sense of the C++ standard, or it just so happens to compile?

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

Here is a second example, which also exploits order of template instantiation:

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),它将无法编译并显示错误:

Here if you swap (1) and (2) it will fail to compile with error:

错误:"B"中没有名为"Q"的类型

error: no type named 'Q' in 'B'

所以问题是:类定义实际上是按标准允许彼此依赖的吗?

So the question is: Is it actually permitted by standard for class definitions to depend on each other like that?

推荐答案

如另一个

As discussed in another answer, CWG 287's resolution is just the de facto approach followed by implementations, mandating that precisely the entities that precede the "inline" PoI of the member being instantiated are in scope.

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

Thus, when the lines are swapped, we attempt to access something that hasn't been instantiated yet:

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 的专业化已被完全实例化,因此没有其他细微之处.)

The sequence (2), (2.1), (2.2) and (2.3) is the order in which the instantiations are caused, with the effective PoI preceding that declaration. With (2) declared first, at (2.3), we reference (*), which isn't in scope. If (1) comes first, then at (1.3) we access (#), which is indeed in scope. (In a subsequent declaration of (2), the specialisation of A has already been instantiated fully, so there are no further subtleties.)

如果您在递归"别名之前声明所有基本案例"别名,这是您的第一个代码片段的区别,那么它可以工作

If you declare all the "base case" aliases before the "recursive" ones, which was the difference in your first snippet, it works fine either way.

这篇关于奇怪地相互重复的类定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆