使用 struct 本身作为模板参数是否合法?

Is it legal to use struct itself as template argument?

根据 cppreference.com 上的 Template parameters and template arguments

A template argument for a type template parameter must be a type-id, which may name an incomplete type

这意味着这个例子(从那个页面复制)是合法的:

// Example 0

template <typename T>
struct X {};

struct A;

int main() {
    X<A> x1;
}

而且,我想,这也是合法的:

// Example 1

template <typename T>
struct X {};

struct A {
    X<A> x1;
};

int main() {
    A a1;
}

现在,让我们把代码复杂一点:

// Example 2

template <typename T>
struct X {
    using type = typename T::type;
};

struct A {
    using type = int;
    X<A>::type x1;
};

int main() {
    A a1;
}

在这种情况下,A 在用作模板参数时仍然是一个不完整的类型,并且代码可以编译。现在,如果我更改 A:

中行的顺序
// Example 3

template <typename T>
struct X {
    using type = typename T::type;
};

struct A {
    X<A>::type x1; // ERROR HERE
    using type = int;
};

int main() {
    A a1;
}

代码不再编译。 Clang 错误消息类似于 no type named 'type' in 'A',还有 GCC、ICC 和 MSVC return 类似的错误(参见 this demo on godbolt.com), and this seems almost identical to that of typedefs on CRTP, discussed here.

无论如何,这种行为似乎是合理的:编译器使用在使用 X<A> 时定义的 A 的所有属性。

但是 C++14(或更高版本)标准对此有何规定?依赖工作示例2的行为是否合法?

这是CWG issue 287

你可以看看那里的推理,但我的理解是,共识是目前技术上的标准不允许你的最后一个或 second-to-last 示例,因为 second-to-last 的实例化点=10=]会在A的定义之前,其中typename T::type无法形成。

但是,如提议的决议中所示,即使实例化点在 A 的定义之前,但在 A 中声明的名称在需要实例化的点之前X<A> 实例化仍然可见。

这就是编译器实现的,您会观察到。