在下面的代码中,命名空间中的独立组件S
具有自己的定义Big
和Small
类型,以及将其拆分为 s 集合的split
函数。Big
Small
S
还提供了另一个函数 ,work
它利用split
, 并且旨在由其S
自身以及其他依赖组件使用, 例如命名空间中的组件,D
假定它们提供自己的定义Big
和Small
以及它们自己的split
定义将通过 ADL 识别。¹
#include <iostream>
#include <string>
#include <utility>
#include <vector>
namespace S /* standalone */ {
struct Big{};
struct Small{};
std::vector<Small> split(Big b) {
std::cout << "S" << std::endl;
return {/* use b to make output */};
}
template<typename BigT>
void work(BigT/* acutally this is a template class with `BigT` as a template paramter*/ x) {
split(x); // correspondingly `split` is not applied on `x` but on the `BigT` which is part of it
// a lot of complex stuff
}
}
namespace D /* dependent on standalone */ {
struct Big{};
struct Small{};
std::vector<Small> split(Big b) {
std::cout << "D" << std::endl;
return {/* use b to make output */};
}
}
int main() {
S::Big line1{};
D::Big line2{};
S::work(line1); // uses S::split
S::work(line2); // uses D::split
}
好吧,实际上S::split
是一个函数对象,而不是一个函数²,
namespace S {
struct Split {
std::vector<Small> operator()(Big) const {
std::cout << "S" << std::endl;
return {};
}
} split;
}
所以 ADL 不起作用。
关于如何解决这些需求的任何建议?
从评论中可以看出,Niebloids 和/或tag_invoke
代表了我的问题的答案。我真的很想更多地了解这些概念。
目前,我对 Niebloids 的理解(我正在阅读Eric Niebler 的这篇博客)是它们是函数对象(当它们在范围内时)阻止 ADL,因此“集中”所有指向不合格自由函数的函数调用与 niebloid 同名;然而,它们operator()
依赖 ADL 将调用转发到适当的自由函数。因此,看起来我的示例代码S::split
中作为函数对象和D::split
作为自由函数之间的对比无法由 niebloids 解决,除非我创建S::split
了一个自由函数(在这种情况下,ADL 在我的简单场景中就足够了)。
¹ 最初是在andwork
中定义的,上面的代码是我尝试的重构,在此期间我遇到了所描述的问题。S
D
² 这样做的原因是,S::split
它在多个上下文中使用S
,它有一些重载operator()
,最重要的是,它经常作为对象传递,非常方便。