12

在重载决议期间,函数对象的处理方式是否与常规函数不同?如果是这样,怎么做?

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

#include <iostream>

namespace N
{
    enum E { A, B };

    void bar(E mode) { std::cout << "N::bar\n"; }
}

template <typename... Args>
void bar(Args&&... args) { std::cout << "global bar\n"; }

int main()
{
    bar(N::A);
}

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

但是,如果我将全局 bar 更改为函数对象,如下所示:

#include <iostream>

namespace N
{
    enum E { A, B };

    void bar(E mode) { std::cout << "N::bar\n"; }
}

struct S
{
    template <typename... Args>
    void operator()(Args&&... args) { std::cout << "global bar\n"; }
};
S bar;

int main()
{
    bar(N::A);
}

输出现在是“全局栏”。为什么有区别?

4

2 回答 2

12

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

3.4.1/3

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

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

于 2012-08-29T04:01:30.100 回答
4

见 3.4.2p3,它说

令 X 为由非限定查找 (3.4.1) 生成的查找集,让 Y 为由参数相关查找生成的查找集(定义如下)。如果 X 包含

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

那么 Y 为空。

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

于 2012-08-30T21:26:44.273 回答