Fed*_*dor 16 c++ friend default-value language-lawyer
在下一个例子中,U带有私有析构函数的类有一个友元函数foo。这个友元函数的参数类型U为默认值U{}:
class U{ ~U(); friend void foo(U); };
void foo(U = {});
Run Code Online (Sandbox Code Playgroud)
Clang 和 MSVC 接受此代码,但 GCC 拒绝它并显示错误
error: 'U::~U()' is private within this context
2 | void foo(U = {});
| ^
Run Code Online (Sandbox Code Playgroud)
演示:https : //gcc.godbolt.org/z/eGxYGdzj3
哪个编译器就在这里,友谊是否扩展到 C++ 中的默认参数?
C++20 [class.access]/8 提供如下:
默认参数 (9.3.3.6) 中的名称在声明点绑定,并且在该点而不是在使用默认参数的任何点检查访问。如 13.9.1 中所述,对函数模板和类模板的成员函数中的默认参数进行访问检查。
然而,[expr.call]/8 说:
... 每个参数的初始化和销毁发生在调用函数的上下文中。[示例:在调用函数的调用点检查构造函数、转换函数或析构函数的访问。...
虽然“示例”文本不是规范性的,但我认为它反映了意图;因此,为了和谐地阅读这两个规定,我们应该理解默认参数类型的析构函数(至少在我看来)不是“默认参数中”的名称。相反,我们应该将调用朋友函数视为发生在以下阶段:
GCC 不应该拒绝声明,void foo(U = {})因为还没有实际使用析构函数;事实上,它可能foo只能从有权访问U::~U. 但是,如果foo从无法访问 的上下文中调用U::~U,则程序应该是格式错误的。在这种情况下,我认为 Clang 和 MSVC 是错误的,因为它们仍然接受代码。
但是,[dcl.fct.default]/5 也存在问题,其中指出:
默认参数具有与参数类型变量声明中的初始化程序相同的语义约束,使用复制初始化语义(9.4)。默认参数中的名称是绑定的,并且在默认参数出现的地方检查语义约束。...
该标准从未定义“语义约束”的含义;如果假设它包含对初始化和销毁的访问控制,那么这可能解释了为什么 Clang 和 MSVC 似乎允许foo从不应访问U::~U.
但是更多地考虑这一点,我觉得这没有太大意义,因为这意味着默认参数是“特殊的”,我认为这不是故意的。也就是说,请考虑:
class U {
public:
U() = default;
U(const U&) = default;
private:
~U() = default;
friend void foo(U);
};
void foo(U = {}) {}
int main() {
auto p = new U();
foo(*p); // line 1
foo(); // line 2
}
Run Code Online (Sandbox Code Playgroud)
在这里,MSVC 接受第 1 行和第 2 行;考虑到 [expr.call]/8 如何要求析构函数可以从main. 但是 Clang 接受第 2 行并拒绝第 1 行,这在我看来也很荒谬:我不认为标准的意图是选择使用默认参数(而不是自己提供参数)会免除调用者必须有权访问参数类型的析构函数。
如果 [dcl.fct.default]/5 似乎需要 Clang 的行为,那么我认为它应该被认为是有缺陷的。
| 归档时间: |
|
| 查看次数: |
319 次 |
| 最近记录: |