英文:
What is a concrete use case of std::ranges::partial_sort_copy with *different* projections?
问题
我找出了我的代码中std::ranges::partial_sort_copy中的投影不匹配的问题。在阅读了cppreference之后,我无法想象出需要proj1和proj2不同的合法情况。
这个算法提供两个不同投影的动机是什么?
英文:
I root caused a problem in my code to mismatched projections in std::ranges::partial_sort_copy.  After reading cppreference,  I couldn't think of a legitimate situation where proj1 & proj2 needed to be different.
What's the motivation for this algorithm providing two different projections?
答案1
得分: 3
以下是翻译好的部分:
当输入范围的类型与输出范围的类型不匹配时,您需要它。
考虑以下示例:
#include <string>
#include <ranges>
#include <algorithm>
#include <iostream>
int main() {
    std::string names[] = {
        "alpha",
        "beta",
        "tau",
        "pi",
        "omega"
    };
    std::string_view shortest_three[3];
    std::ranges::partial_sort_copy(names, shortest_three,
                                   {},                         // std::ranges::less 
                                   &std::string::length,       // proj1
                                   &std::string_view::length); // proj2
    // 注意:在 std::string/std::string_view 中可能不允许获取成员函数的地址。
    // (未指定的行为,请参见 [namespace.std]/6)
    // 为了安全起见,我们应该将对 length() 的访问包装在 lambda 表达式中。
    for (auto view : shortest_three) {
        std::cout << view << '\n';
    }
}
这将输出:
pi
tau
beta
在此示例中,如果目标范围包含 std::string 对象,将是浪费的,因为我们可以将最短的三个字符串存储为 const char* 或 std::string_view。因此,这里应用了两个不同的投影。
从技术上讲,您总是可以创建一个涵盖两种情况的单一投影:
auto proj = []<typename T>(const T& x) {
    // 注意:在这种特定情况下,我们不需要此 if 语句,
    // 因为成员函数的名称对于两种类型都是完全相同的。但是,这可能并不总是如此,
    // 例如,对于一种类型我们需要 .length(),而对于另一种类型我们需要 .size()。
    if constexpr (std::is_same_v<T, std::string>) {
        return x.length();
    }
    else {
        return x.length();
    }
};
但是,通常提供两个单独的投影更加简洁。
英文:
You need it when there is a mismatch between the type of the input range, and the type of the output range.
Consider the following example:
#include <string>
#include <ranges>
#include <algorithm>
#include <iostream>
int main() {
    std::string names[] = {
        "alpha",
        "beta",
        "tau",
        "pi",
        "omega"
    };
    std::string_view shortest_three[3];
    std::ranges::partial_sort_copy(names, shortest_three,
                                   {},                         // std::ranges::less 
                                   &std::string::length,       // proj1
                                   &std::string_view::length); // proj2
    // note: It might not be allowed to take the address of
    //       member functions in std::string/std::string_view.
    //       (unspecified behavior, see [namespace.std]/6)
    //       To be on the safe side, we would have to wrap access to length()
    //       in a lambda expression.
    for (auto view : shortest_three) {
        std::cout << view << '\n';
    }
}
This outputs:
pi
tau
beta
In this example, it would be wasteful if the destination range contained std::string objects, because we could just as well store the shortest three strings as a const char* or std::string_view.
As a result, there are two different projections applied here.
Technically you could always create a single projection that covers both cases:
auto proj = []<typename T>(const T& x) {
    // note: in this specific case, we don't need this if-statement,
    //       because the name of the member function is the exact same for
    //       both types. However, this may not always be the case, e.g.
    //       we need .length() for one type and .size() for another.
    if constexpr (std::is_same_v<T, std::string>) {
        return x.length();
    }
    else {
        return x.length();
    }
};
However, providing two separate projections is often more concise.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论