函数对象如何影响过载分辨率



在重载解析过程中,函数对象的处理方式是否与常规函数不同?如果是,如何?

我遇到过以下情况,用等价可调用的函数对象替换函数会改变代码的含义:

#include <iostream>
namespace N
{
    enum E { A, B };
    void bar(E mode) { std::cout << "N::barn"; }
}
template <typename... Args>
void bar(Args&&... args) { std::cout << "global barn"; }
int main()
{
    bar(N::A);
}

这里的输出是"N::bar"。到目前为止,ADL正在找到非常好的:N::bar,N::bar和全局bar都是完全匹配的,并且N::bar是首选,因为它不是模板。

但如果我将全局条更改为函数对象,就像这样:

#include <iostream>
namespace N
{
    enum E { A, B };
    void bar(E mode) { std::cout << "N::barn"; }
}
struct S
{
    template <typename... Args>
    void operator()(Args&&... args) { std::cout << "global barn"; }
};
S bar;
int main()
{
    bar(N::A);
}

输出现在是"全局条"。为什么有区别?

这里的重要一点是,只有当查找确定名称是函数调用中的函数时,ADL才会生效。在第二种情况下,发现bar对象而不是函数,因此表达式bar(N::A)不是函数调用,而是operator()对对象bar的应用。因为它不是函数调用,所以ADL不会启动,N::bar也不会被考虑。

3.4.1/3

3.4.2中描述了查找用作函数调用后缀表达式的非限定名称。[注意:为了确定(在解析过程中)表达式是否是函数调用的后缀表达式,适用通常的名称查找规则。3.4.2中的规则对表达式的语法解释没有影响。

另一种方法是注意到ADL将向重载函数的集合添加新函数,但在第二个示例中没有这样的集合:查找查找找到一个对象,并调用该对象的一个成员。

参见3.4.2p3,其中说明

设X是由非限定查找(3.4.1.)产生的查找集,设Y是由参数相关查找(定义如下)产生的查询集。如果X包含

  • 既不是函数也不是函数模板的声明

则Y为空。

如果没有这样的规则,你是对的:ADL会将你的其他函数添加到重载集。事实上,13.3.1.1p1依赖于此:它有两个分支;一个用于函数调用表达式,其中操作数表示类对象,另一个用于操作数表示一个或多个函数或函数模板。

最新更新