类外功能重载未见

问题描述 投票:0回答:1

考虑以下代码片段。

enum class Bar {
    A
};

void foo(Bar) {}

struct Baz {
    void foo() {
        foo(Bar::A);
    }
};

编译失败,gcc9.2的信息是:

:12:19: error: no matching function for call to 'Baz::foo(Bar)'
 12 |         foo(Bar::A);
    |              

我不怀疑这是一个bug,因为clang 10也失败了。对于这种情况,我有两个问题。

  1. 标准在哪里定义了这种重载的行为?

  2. 编译器的行为被这样定义的原因是什么?

实例

c++ language-lawyer
1个回答
5
投票

根据非限定名查找规则,从标准。[basic.lookup.unqual]1,

(重点是我)

在[basic.lookup.unqual]中列出的所有情况下,作用域都会按照各自类别中列出的顺序搜索声明。一旦找到了name的声明,name查找就会结束。.

这意味着这个名字 foo 是在类的作用域中找到的(即在 Baz::foo 本身),那么名称查找就会停止;全局的名称不会被找到,也不会被考虑到后面发生的重载解决。

关于你的第2个问题,函数不能通过不同的作用域进行重载,这可能会造成不必要的混乱和复杂。请看下面的代码。

struct Baz {
    void foo(int i) { }
    void foo() {
        foo('A');
    }
};

你知道 'A' 将转换为 int 然后传给 foo(int),这很好。如果允许函数通过作用域重载,如果有一天一个 foo(char) 在全局范围内被某个人或库添加,代码的行为会发生改变,这是很混乱的,特别是当你不知道添加全局的时候。


6
投票

调用 foo 里面 Baz::foo() 只会查询类内的名称。如果您想使用 foo 类外声明 Baz,你需要使用作用域解析运算符,像这样。

enum class Bar {
    A
};

void foo(Bar) {}

struct Baz {
    void foo() {
        ::foo(Bar::A);   // looks up global 'foo'
    }
};

请注意,对 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
    }
};

以下是引用自 cppreference 对于 类定义.

e) 如果这个类是一个命名空间的成员,或者是嵌套在一个命名空间成员的类中,或者是一个命名空间成员的函数中的一个局部类,则会搜索命名空间的范围,直到该类、包围类或函数的定义,如果查找的是一个友情声明引入的名字:这种情况下只考虑最里面的包围命名空间,否则照样继续查找包围命名空间,直到全局范围。

当然,这只适用于类定义,但对于 成员功能 (这是你的例子),它说

对于成员函数体内部使用的名称、成员函数的默认参数、成员函数的异常规范或默认成员初始化器,搜索的作用域与[类定义]中相同,......。

所以同样的逻辑也适用。

© www.soinside.com 2019 - 2024. All rights reserved.