bar*_*top 1 c++ overloading language-lawyer name-lookup
考虑以下代码片段:
enum class Bar {
A
};
void foo(Bar) {}
struct Baz {
void foo() {
foo(Bar::A);
}
};
Run Code Online (Sandbox Code Playgroud)
它无法编译,来自 gcc 9.2 的消息是:
:12:19: error: no matching function for call to 'Baz::foo(Bar)'
12 | foo(Bar::A);
|
Run Code Online (Sandbox Code Playgroud)
我不怀疑这是一个错误,因为 clang 10 也失败了。关于这种情况,我有两个问题:
标准在哪里定义了这种重载的行为?
以这种方式指定编译器行为的可能原因是什么?
对foo
inside的调用Baz::foo()
只会在类中查找名称。如果您打算使用foo
class 之外的声明Baz
,则需要使用范围解析运算符,如下所示:
enum class Bar {
A
};
void foo(Bar) {}
struct Baz {
void foo() {
::foo(Bar::A); // looks up global 'foo'
}
};
Run Code Online (Sandbox Code Playgroud)
请注意,无作用域调用foo
失败,因为Bar::foo
在最近的作用域中找到了 。如果您以不同的方式命名函数,则在 中找不到函数Bar
,编译器将在函数的外部范围内查找。
enum class Bar {
A
};
void foo(Bar) {}
struct Baz {
void goo() { // not 'foo'
foo(Bar::A); // this is fine, since there is no 'Bar::foo' to find
}
};
Run Code Online (Sandbox Code Playgroud)
这是来自cppreference的类定义的引用。
e) 如果这个类是命名空间的成员,或者嵌套在作为命名空间成员的类中,或者是作为命名空间成员的函数中的局部类,则搜索命名空间的范围,直到类、封闭类或函数的定义。如果查找由朋友声明引入的名称:在这种情况下只考虑最内部的封闭命名空间,否则查找将继续封闭命名空间,直到像往常一样全局范围。
当然,这仅适用于类定义,但对于成员函数(这是您的示例),它说
对于成员函数体内部使用的名称、成员函数的默认参数、成员函数的异常规范或默认成员初始值设定项,搜索的范围与[类定义]中的相同,...
所以同样的逻辑适用。
根据非限定名称查找规则,从标准[basic.lookup.unqual]/1,
(强调我的)
在 [basic.lookup.unqual] 中列出的所有情况下,按照每个类别中列出的顺序搜索范围以查找声明;一旦找到 name 的声明, name 查找就会结束。
这意味着名称foo
在类范围内(即它Baz::foo
本身)找到,然后名称查找停止;对于稍后发生的重载决议,将不会找到并考虑全局一个。
关于你的第二个问题,函数不能通过不同的范围重载;这可能会导致不必要的混乱和复杂性。考虑以下代码:
struct Baz {
void foo(int i) { }
void foo() {
foo('A');
}
};
Run Code Online (Sandbox Code Playgroud)
你知道'A'
会被转换为int
然后传递给foo(int)
,那很好。如果允许通过作用域重载函数,如果有一天某foo(char)
个人或库在全局作用域中添加了a ,则代码的行为会发生变化,这非常令人困惑,尤其是当您不知道添加全局作用域时。