Clang vs. GCC:通过限定/非限定名将全局函数定义为朋友

Clang vs. GCC: Friend-ing a global function via qualified/unqualified names

本文关键字:全局 函数 朋友 定义 GCC vs Clang      更新时间:2023-10-16

我正试图弄清楚是否要对Clang, GCC或两者都提交错误报告(我已经对Clang主干和GCC 4.7.2进行了测试:如果有人可以对GCC主干进行验证,那将是有帮助的):

基本上,以下代码三行文件在-fsyntax-only,默认模式和c++ 11模式下编译良好:

class A {
    friend void f();
};

注意,没有预先声明f(),但这是明确的OK。

但是,Clang(而不是GCC)拒绝以下命令:

class A {
    friend void ::f();
};

Clang的错误是"在指定的范围内没有发现类型为'void()'的名为'f'的函数",但我找不到任何理由在标准中处理这种情况与前一个不同,所以我认为这是一个bug;我可能是错的(我读的是N3242,然而,据我所知,这是c++ 11之前的最后一个公开草案)。

然而,下一个示例被GCC拒绝,而不是Clang拒绝:

void f() { }
void g()
{
    class A {
        friend void ::f();
    };
}

GCC的错误是"朋友声明' void f() '在局部类中没有事先声明",这似乎没有意义,因为void ::f()应该指的是全局命名空间中的f(),这是声明的。(不要介意friend从一个局部类生成一个全局函数是荒谬的,它似乎并不违法…)

最后,Clang和GCC都拒绝了最后这个例子:

void g()
{
    class A {
        friend void ::f();
    };
}

错误分别是"未事先声明的局部类中的友元声明'void f() '"answers"在指定范围内未找到类型为'void()'的名为'f'的函数"。现在,从11.4p11开始,确实有理由拒绝局部类中先前未声明的函数的友元声明,但该段实际上是:

如果友元声明出现在局部类(9.8)中,并且指定的名称是非限定名,则优先查找声明时不考虑最内层封闭非类作用域之外的作用域。对于友元函数声明,如果没有先前的声明,则程序是病态的…

这个声明的违法性显然是基于这段话的第二句,但我不清楚这句话是否也应该适用于限定名的情况,就像在这个例子中使用的那样。(可以说,不管是否使用了"非限定名",整个段落都可能适用于"局部类"的情况,而"and the name specified is an非限定名"子句只适用于第一句中描述的查找,但这似乎有点夸张……我想象这种可能性的唯一原因是两个编译器都拒绝它。)

无论如何,在我看来,这四种情况(不管它们是否合理)都应该是合法的;即使没有,Clang和GCC中至少有一个在第二种和第三种情况下是错误的。有人能在我的逻辑中找出错误吗?

第三和第四种情况无可否认是荒谬的;但是我可以看到第二种情况破坏了某人的有效和有用的代码,所以我有点惊讶它从来没有被抓住,如果它确实是一个bug。

我认为8.3/1中的这句话是相关的:

声明符-id是限定符时,声明应该指向限定符所指向的类或命名空间(或者,在命名空间的情况下,指向该命名空间的内联命名空间集的元素)的先前声明的成员,或者指向其专门化;成员不能仅仅由using-declaration声明符id嵌套名称说明符指定的类或命名空间范围内引入。

本段讨论的是任何类型声明中的声明符。所以我认为例子2和4是错误的,clang是正确的,而gcc是错误的。

(如果全局声明是一个模板函数,例3可能更现实。将全局函数模板或局部类的专门化设为好友可能会很有用。如果这是允许的,你的例子3也可能是合法的,以保持一致性。