使用 std::function 的 ADL:可以通过 std::function 的参数列表中的类型找到采用 std::function 对象的函数吗?

ADL with std::function: Can functions taking std::function objects be found via the types in the std::function's argument list?

考虑以下代码片段:

#include <functional>

namespace ns {
    struct Arg{};
    using Func = std::function<int(Arg)>;

    Func operator+(Func lhs, Func rhs) {
        return [lhs, rhs](Arg arg) {
            return lhs(arg) + rhs(arg);
        };
    }
}

int main() {
    ns::Func foo = [](ns::Arg i) {return 5;};
    ns::Func bar = [](ns::Arg i) {return 2;};
    auto foobar = foo + bar;
    return foobar(ns::Arg());
}

以上代码可以使用各种编译器进行编译。相反,以下代码片段无法编译。唯一的区别是 Func 中使用的参数类型(Arg vs int):

#include <functional>

namespace ns {
    using Func = std::function<int(int)>;

    Func operator+(Func lhs, Func rhs) {
        return [lhs, rhs](int i) {
            return lhs(i) + rhs(i);
        };
    }
}

int main() {
  ns::Func foo = [](int i) {return i + 5;};
  ns::Func bar = [](int i) {return i * 2;};
  auto foobar = foo + bar; // BANG! Error here!
  return foobar(2);
}

我理解后一个版本的错误:被调用的operator+定义在命名空间中,因此在没有明确指定命名空间的情况下找不到。参数相关查找在这里无济于事,因为 operator+ 是在与参数类型不同的命名空间 (ns) 中定义的(std::function 是在 namespace std 中定义的, using 声明与此无关)。

但为什么在 Func 接受参数 ns::Arg 的情况下找到正确的 operator+Func 的命名空间没有改变。根据 C++ 标准,使用 Arg 的代码是否有效?

Is the code using Arg valid according to the C++ standard?

是的。根据 [basic.lookup.argdep/2.2]

,ADL 的关联命名空间包括专门化的任何模板参数的关联命名空间

... Furthermore, if T is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces of which any template template arguments are members; and the classes of which any member templates used as template template arguments are members. [ Note: Non-type template arguments do not contribute to the set of associated namespaces. — end note ]

std::function<int(Arg)> 是 class 模板特化,ns 与其参数之一相关联。因此,ns 包含在 ADL 搜索 operator+ 的命名空间集中。

这条规则的存在是为了让可重用的组件更有用。这个想法是允许一个库公开一个 API ,它采用 std::unique_ptr<ns::Foo> 作为句柄类型,并让 ADL 在出现句柄时从 ns 中获取正确的函数。