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



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

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
    
    如果在命名空间范围中定义的函数的decl说明符seq或参数声明子句中使用了精化类型说明符,则标识符在包含声明的命名空间中声明为类名;否则,除了作为友元声明之外,标识符是在包含声明的最小命名空间或块范围中声明的

但你正在寻找最新的最棒的草稿头。你找不到它,因为草案中合并了P1787。它改变了规范性措辞,并将其移动,目的是解决一些悬而未决的措辞问题,并在存在模块的世界中改进标准的方法。

如今,相关部分位于中

[dcl.type.elab]

3否则,详细的类型说明符E不应具有属性说明符seq。如果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;
}

它产生了大量的诊断,坦率地说,这些诊断似乎是不合理的。IMHO,这是C的一个缺点,这反过来又足够激励C++按照它的方式做事。用C++编译器编译完全相同的程序,它的格式很好。

class s是一个正向声明。这相当于

class s;
void f(s);

s不是一个变量的名称,但它是一个类型。所以你只是说函数f取一个类型为s的参数。下一行告诉u等同于s。没问题。你不需要完整的定义就可以做到这一点。

最新更新