在函数参数列表中向前声明的类型如何在函数作用域之外可见?

问题描述

以下程序编译,我觉得很奇怪。

void f(class s);
using u = s;      // ok,but why?

s函数参数列表中类的前向声明,在我看来它不应该在函数作用域之外可见。

basic.scope.param 似乎是我能找到这条规则的明显地方,但我无法解决。措辞可能在 dcl.dcl 中的某个位置,但我不知道从哪里看。

什么规则涵盖了这个?或者,对为什么此规则存在的解释会很好。

解决方法

首先,这条规则并不是特别新。它几乎从 C++ 诞生以来就存在了。至于C++20,则是这样写的:

[basic.scope.pdecl]

7 首先在详细类型说明符中声明的类的声明点如下:

  • ...
  • 用于表单的详细类型说明符
    class-key identifier
    
    如果在命名空间范围内定义的函数的声明说明符序列或参数声明子句中使用了详细类型说明符,则该标识符在包含该声明的命名空间中被声明为类名;否则,除非作为友元声明,标识符在包含该声明的最小命名空间或块作用域中声明。

但您正在寻找最新的最佳选秀头。您找不到它,因为草案已合并 P1787。它更改了规范性措辞并对其进行了移动,目的是解决一些突出的措辞问题并在存在模块的世界中改进标准的方法。

今天,相关部分驻留在

[dcl.type.elab]

3 否则,详细类型说明符 E 不应具有属性说明符序列。如果 E 包含一个标识符但没有嵌套名称说明符并且(非限定的)对该标识符的查找没有找到,则 E 不应由 enum 关键字引入并将标识符声明为类名。 E 的目标作用域是最近的封闭命名空间或块作用域。

从本质上讲,它的含义与 C++20 的措辞相同。它像通过前向声明一样将类名引入最近的封闭范围。


至于为什么会有这个规则。嗯......它不存在于最新的 C 中。这为初学者带来了一些相当晦涩的问题。考虑这个简单的程序:

void func(struct foo*);

struct foo { int bar; };

int main() {
  struct foo f;
  func(&f);
}

void func(struct foo* pf) {
  pf->bar = 0;
}

它产生一个 slew of diagnostics,坦率地说,这似乎不合理。恕我直言,这是 C 的一个缺点,而这反过来又足以激励 C++ 以它的方式做事。用 C++ 编译器编译完全相同的程序,它是 well formed

,

class s 是前向声明。这相当于

class s;
void f(s);

s 不是变量的名称,而是一种类型。所以你只是说函数 f 接受一个 s 类型的参数。 下一行还说明 u 等价于 s。没问题。你不需要完整的定义来做到这一点。