1

在下面的代码中,命名空间中的独立组件S具有自己的定义BigSmall类型,以及将其拆分为 s 集合的split函数。BigSmall

S还提供了另一个函数 ,work它利用split, 并且旨在由其S自身以及其他依赖组件使用, 例如命名空间中的组件,D假定它们提供自己的定义BigSmall以及它们自己的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中定义的,上面的代码是我尝试的重构,在此期间我遇到了所描述的问题。SD

² 这样做的原因是,S::split它在多个上下文中使用S,它有一些重载operator(),最重要的是,它经常作为对象传递,非常方便。

4

0 回答 0