友元声明及其表示的实体

3

我有一个关于友元函数/类的问题。考虑以下代码:

#include <iostream>

struct A
{
private:
    int a = 5;
    friend int foo(A a);
};

int foo(A a)
{
    return a.a;
}

int a = foo(A());

int main(){ std::cout << a << std::endl; }
演示

它运行正常,类范围内和全局范围内的int foo()指向同一实体。尽管在类范围内声明int foo()并没有在全局范围内引入名称。如果是这样,我们将在代码中收到链接错误而不是编译错误:

#include <iostream>

struct A
{
private:
    int a = 5;
    friend int foo();
};

int a = foo(); //undeclared foo

int main(){ std::cout << a << std::endl; }

演示

在标准中我没有找到这方面的解释。它说的是 N3797:11.3/6 [class.friend]:

如果且仅当类是非本地类(9.8),函数名未限定且具有命名空间作用域时,函数可以在类的友元声明中被定义。

因此,它解释了为什么下面的代码可以正常工作:

#include <iostream>

struct A
{
private:
    int a = 5;
    friend int foo(A a)
    {
        return a.a;
    }
};

int a = foo(A());

int main(){ std::cout << a << std::endl; }

演示

我们在友元声明中定义了函数,并且,正如标准所述,它成为了全局命名空间的成员。但是这个规则只涵盖定义,而不是声明。显然,并非所有的声明都是定义。因此,我们不能将其应用于第一个例子。


1
[命名空间.成员定义]/p3. - T.C.
@T.C. 非常有帮助,一如既往,谢谢。 - user2953119
1个回答

0

在上一个程序中

#include <iostream>

struct A
{
private:
    int a = 5;
    friend int foo(A a)
    {
        return a.a;
    }
};

int a = foo(A());

int main(){ std::cout << a << std::endl; }

编译器仅通过参数相关的查找才能找到函数foo的定义。 您应该考虑C++标准中的以下部分:
3.4.2 参数相关名称查找
对于函数调用中的每个参数类型T,都有一组零个或多个相关的命名空间和一组零个或多个相关的类需要考虑。这些命名空间和类的集合完全由函数参数的类型(以及任何模板模板参数的命名空间)确定。用于指定类型的typedef名称和using声明不会对此集合产生影响。
因此,在此程序中,编译器在类的范围内搜索函数,因为函数的参数具有类类型。如果您将函数定义更改为以下方式,则编译器将无法找到该函数。
#include <iostream>

struct A
{
public:
    A( int x ) : a( 2 * x ) {}

private:
    int a;
    friend int foo( int x )
    {
        A a( x );

        return a.a;
    }
};

int a = foo( 10 );


int main() 
{
    std::cout << a << std::endl;

    return 0;
}

但是,如果您在定义类的范围内添加函数声明,则编译器将看到该函数。

#include <iostream>

struct A
{
public:
    A( int x ) : a( 2 * x ) {}

private:
    int a;
    friend int foo( int x )
    {
        A a( x );

        return a.a;
    }
};

int foo( int );

int a = foo( 10 );


int main() 
{
    std::cout << a << std::endl;

    return 0;
}

这里是程序的输出
20

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接