我有以下 c++ 代码段。main()
在函数内部声明了一个类。
我们不能在本地类中定义友元函数的原因是什么?
#include<iostream>
int main()
{
class Foo
{
void foo() {} // Ok
friend void Bar(){}; // Error
};
}
我有以下 c++ 代码段。main()
在函数内部声明了一个类。
我们不能在本地类中定义友元函数的原因是什么?
#include<iostream>
int main()
{
class Foo
{
void foo() {} // Ok
friend void Bar(){}; // Error
};
}
有一个实际的原因。首先,内联好友定义无法通过合格或不合格查找找到。它只能由 ADL 找到。因此,如果我们从您的示例中获取该类,请将其放在全局范围内并尝试调用Bar
:
class Foo
{
friend void Bar(){};
void foo() {
Bar();
}
};
我们将收到Bar
未在该范围内声明的通知。因此,如果它是在本地课程中。你不能从成员那里调用它。你不能在函数内部调用它。你可以称之为它的唯一方法涉及一些篮球或 ADL。所以语言就是不允许的。它不被视为有用的功能。
这没有令人信服的技术原因。ADL找不到它,你说?好吧,这基本上是问题的一部分:为什么ADL 找不到它?只需扩展 ADL 以使其找到它。
这种限制的一个设计级别原因可能源于对朋友声明中不合格名称的语言处理。
本地类中友元声明中使用的非限定名称是指来自最近的封闭非类范围的名称。这种对非限定名称的处理非常重要,因为这是本地类可以相互引用的唯一方式(由于显而易见的原因,本地类没有限定名称)
int main()
{
class B;
class A {
int x;
friend B; // refers to local `B`
};
class B {
void foo(A &a) { a.x = 42; }
};
}
此规则也适用于具有非限定名称的友元函数声明。C++ 没有局部函数,但是这样的友元声明仍然可以引用函数的局部非定义声明(这是完全合法的)
void foo() {}
int main()
{
void foo(); // refers to `::foo`
class A {
friend void foo(); // refers to local `foo`, which is `::foo`
};
}
现在,如果在本地类中定义友元函数(当然是使用非限定名称) ,你会建议发生什么?这样的声明引入了什么功能?具体到什么范围?根据定义,它不是类的成员,因为友元声明不引入类成员。它不能是最近的封闭本地范围的成员,因为这会使它成为本地函数,而 C++ 不支持本地函数。
我们不能只是统一地改变友元声明中所有非限定名称的行为,并说它们现在应该引用最近的封闭命名空间范围内的名称,因为这将导致无法引用本地类(如上所示)。
摆脱这种情况的唯一方法是只让类内友元函数定义引用(并定义)最近的封闭命名空间范围内的函数。此类函数只能通过 [修改后的] ADL 调用(假设我们可以接受)。但这意味着我们必须对友元函数定义中的非限定名称给予不同的处理(与非定义友元声明相反)。这将是相当不雅和令人困惑的。因此,语言作者决定反对它。
请注意,在 C++14 之后,这一点的重要性可能会显着增加,这给了我们auto
在函数中推导出的返回类型。在那之后,本地课程变得不再像以前那样“本地化”了
auto foo()
{
struct S // Local class
{
void bar() {}
};
return S();
}
int main()
{
auto a = foo();
a.bar(); // An object of local class used outside of its original scope
typedef decltype(a) S; // Local type is "stolen" from its original scope
S b; // and used to freely declare objects in a completely
b.bar(); // different scope
}
因为本地类的成员函数必须完全在类主体和友元函数中定义,而不是成员函数。我们在类内声明友元函数并在类外定义。
根据cppreference:
本地课程
- 本地类不能有静态成员
- 本地类的成员函数没有链接
- 本地类的成员函数必须完全在类体内定义
- 闭包类型以外的本地类 (C++14 起) 不能有成员模板
- 本地类不能有朋友模板
- 本地类不能在类定义中定义友元函数
- 函数内部的本地类(包括成员函数)可以访问与封闭函数可以访问的名称相同的名称。