使用 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
中获取正确的函数。
考虑以下代码片段:
#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
中获取正确的函数。