考虑标准sec 3.4.1 / 3中的示例:
typedef int f;
namespace N
{
struct A
{
friend void f(A &);
operator int();
void g(A a)
{
int i = f(a);// f is the typedef, not the friend
// function: equivalent to int(a)
}
};
}
f(a)是后缀表达式.编译器如何确定f(a)不是函数调用?我想知道我们什么时候没有像先前声明的typedef int f那样的错误;如下例所示:
#include <stdio.h>
typedef int foo; //error: previous declaration of ‘typedef int foo’
class B
{
public:
friend void foo(B b){ printf("3"); } //error: ‘void foo(B)’ redeclared as different kind of symbol
static const int c=42;
};
int main(){ }
解决方法:
(在我的C 11文档版本中,示例在3.4.1 / 3中提供).
3.4.1 / 3明确指出,为了解析的目的,为了执行是否是后缀表达式或函数调用的初始确定,执行通常的名称查找. “常用”表示按3.4.1的其余部分所述执行查找,并且在该初始阶段不使用ADL. 3.4.1 / 3明确指出“3.4.2中的规则对表达式的句法解释没有影响.” (3.4.2是ADL).
在此示例中,在解析f(a)时,通常使用查找来查找名称f.它找到全局typedef-name :: f而不是其他内容.这意味着f(a)被视为后缀表达式(强制转换),而不是函数调用.注意,在A中的函数f的朋友声明引用函数N :: f,但是它没有向N引入N :: f的声明.因为N :: f函数没有在N中显式声明(它是不可见的在N)中,通常的查找不会看到它.它只能看到global :: f,它是一个typedef-name.
如果您希望通常的名称查找在第一个示例中找到该函数,则必须明确地在N中声明该函数
typedef int f;
namespace N
{
struct A; // <- added
void f(A &); // <- added
struct A
{
friend void f(A &);
...
现在,N :: f的声明在N中可见.现在通常的名称查找将找到N :: f并从一开始就将f(a)视为函数调用.
你的第二个例子与第一个例子有很大不同.你没有额外的命名空间.因此,B中的友元函数声明引用了全局:: foo并声称foo是一个函数.但是global :: foo已经被声明为typedef-name.这种矛盾恰恰是导致错误的原因.
(有趣的是,标准的C 03版本包含3.4.1 / 3中的一个例子,这基本上等同于你的第二个例子.即标准中的例子是不正确的.这被报告为标准的Defect #139.)