有没有办法从比较谓词中了解容器?

问题描述 投票:0回答:1

在下面的代码中,

values
向量包含由
indexes
索引的数据。它对于排序或搜索效果很好,而我必须一次处理比较器中的一个值(实际上是一对)。当我想在对索引进行排序时使用数组元素序列时,问题就出现了;它们必须不是按值排序,而是按值序列排序。

问题(我想要实现的目标)

  1. 是否有一种直接的方法可以使用下面的方法将这些信息存储在比较器中,而无需将向量作为比较器参数传递。也许,这些信息可以以某种方式从投影/视图/等中“提取”?
  2. 有没有办法处理
    std::ranges::equal_range
    中比较的值(见下文),以便我可以将其保留在
    std::ranges::equal_range
    调用的适当位置?

肮脏且危险的解决方法

  1. 我可以使用下面的
    ChainComparator
    来实现这个技巧,它捕获向量并使用其中的信息,这适用于排序,但不适用于
    std::ranges::equal_range
    ,因为要比较的值不属于值向量。请参阅下面的代码。 (演示)。
  2. 我可以将该值作为
    ChainComparator
    的第二个参数传递,并使用“特殊值”(如 -1),如果我在比较器中看到它,则从此参数中获取比较值。但这确实是肮脏的黑客行为,在我的例子中我不能有像 -1 这样的特殊值。 (如果需要的话我可以做一个演示)。
  3. 我可以将常规数据中必须缺少的停止值添加到向量的末尾,并通过停止值检查向量的末尾,但并不总是可能具有这样的“特殊值”(使用所有值)并且大多数时候向量是恒定的。

我想避免什么

  1. 我不想将向量作为参数传递给
    ChainComparator
    ,因为如果开发人员提供错误的向量,它很容易出错。当然,这可以在比较器中检查,但仅在执行时间中检查,并且成本高昂。
  2. 我不想将搜索值作为
    ChainComparator
    参数传递,因为这会使比较器不那么通用,并且会违反“单一任务”规则。最好保留原来的搜索语法。此外,我有很多比较器,当参数化谓词通过我的函数层次结构时,向每个比较器引入这样的支持成本高昂且容易出错。

代码

auto make_index_projection = []<std::ranges::random_access_range R>(R & range) {
    return [&range](std::ranges::range_difference_t<R> i) -> decltype(auto) {
        return std::ranges::begin(range)[i];
        };
};

class ChainComparator {
    const std::vector<int>& vec;
public: 
    ChainComparator(const auto& vec) : vec(vec) {}

    bool operator() (int& lhs, int& rhs) {
        auto lhs_range_begin = vec.begin() + (&lhs - std::data(vec));
        auto rhs_range_begin = vec.begin() + (&rhs - std::data(vec));

        return std::lexicographical_compare(lhs_range_begin, vec.end(), rhs_range_begin, vec.end());
    }
};

int main()
{
    std::vector<int> values = { 0,30,20,40,10 };
    std::vector<int> indexes = { 3,4,2,1,0 };

    auto index_projection = make_index_projection(values);

    std::ranges::sort(indexes, ChainComparator(values), index_projection);

    auto indexing_view = indexes | std::views::transform(index_projection);

    int value_to_search = 30;
    // Error with value here
    auto range_pred = std::ranges::equal_range(indexing_view, value_to_search, ChainComparator(values));

    std::vector<int> values_to_search = { 20, 40 };
    // Error with value here
    auto range_pred_seq = std::ranges::equal_range(indexing_view, values_to_search, ChainComparator(values));
}
c++ stl std std-ranges
1个回答
0
投票

当然这是可行的。毕竟,范围确实非常强大且灵活:)

make_index_projection
现在返回范围中的单个左值,但您也可以返回子范围,这是从指定元素到您感兴趣的任何位置的视图。在您的示例代码中,您似乎感兴趣的是从给定元素到末尾的子范围,所以让我们也这样做。为了更好的演示,我使用
std::string
(一系列
char
,而不是一系列
int
),因为打印
string
更容易。

演示:https://godbolt.org/z/oWa6n47j1

我将编辑答案以添加更多详细信息,但上面的演示已包含所有信息。

© www.soinside.com 2019 - 2024. All rights reserved.