为什么使用通用lambda对字符串向量进行排序不起作用?

huangapple go评论235阅读模式
英文:

Why sorting a vector of strings with generic lambda doesn't work?

问题

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

bool sortByName(const auto& first, const auto& second) {
    return first < second;
}

void print(const auto& vec) {
    for(const auto& s : vec)
        cout << s << ", ";
    cout << endl;
}

int main() {
    vector<string> vec2 = {"sam", "zach", "adam", "peter"};
    print(vec2);

    auto cmp = [](const auto& s1, const auto& s2) {
        return s1 < s2;
    };
    std::sort(vec2.begin(), vec2.end(), cmp);          // line (1)
    print(vec2);

    auto cmp2 = [](const string& s1, const string& s2) {
        return s1 < s2;
    };
    std::sort(vec2.begin(), vec2.end(), cmp2);         // line (2)
    print(vec2);
}

关于为什么在 cmp2 中需要显式提供类型 "string" 以使排序工作的问题,是因为C++中的lambda表达式(匿名函数)需要知道参数的类型。在 cmp 中,使用了 "const auto&",这意味着编译器会根据传入的参数类型来确定参数类型。但在 cmp2 中,使用了 "const string&",这里明确指定了参数类型为 "string",因此编译器知道应该处理字符串类型的参数。如果不指定类型,编译器将无法确定参数的类型,从而无法正确地比较字符串并进行排序。

英文:
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;string&gt;
#include &lt;algorithm&gt;

using namespace std;

bool sortByName(const auto&amp; first, const auto&amp; second) {
    return first &lt; second;
}

void print(const auto&amp; vec) {
    for(const auto&amp; s : vec)
    cout &lt;&lt; s &lt;&lt; &quot;, &quot;;
    cout &lt;&lt; endl;
}

int main() {
    vector vec2 = {&quot;sam&quot;, &quot;zach&quot;, &quot;adam&quot;, &quot;peter&quot;};
    print(vec2);

    auto cmp = [](const auto&amp; s1, const auto&amp; s2) {
        return s1 &lt; s2;
    };
    std::sort(vec2.begin(), vec2.end(), cmp);          // line (1)
    print(vec2);

    auto cmp2 = [](const string&amp; s1, const string&amp; s2) {
        return s1 &lt; s2;
    };
    std::sort(vec2.begin(), vec2.end(), cmp2);         // line (2)
    print(vec2);
}

For the above code snippet, the results are as below,

  1. std::sort with cmp2 sorts the vector i.e. {adam, peter, sam, zach,}
  2. std::sort with cmp keeps the elements of vector in same order, as the original vector i.e. {sam, zach, adam, peter,}

Why do we have to explicitly provide the type i.e. "string" in cmp2 to make this sort work?

答案1

得分: 2

vector vec2 = {"sam", "zach", "adam", "peter"};
This makes vec2 into a std::vector<const char*>.
You are therefore comparing const char* in the generic lambda and not what the pointers are actually pointing at.
In order to make it work without converting the const char*s to std::strings (or better, std::string_views), you could use std::strcmp:
#include ;
auto cmp = [](const char* s1, const char* s2) {
return std::strcmp(s1, s2) < 0;
};

英文:
vector vec2 = {&quot;sam&quot;, &quot;zach&quot;, &quot;adam&quot;, &quot;peter&quot;};

This makes vec2 into a std::vector&lt;const char*&gt;.

You are therefore comparing const char* in the generic lambda and not what the pointers are actually pointing at.

In order to make it work without converting the const char*s to std::strings (or better, std::string_views), you could use std::strcmp:

#include &lt;cstring&gt;

auto cmp = [](const char* s1, const char* s2) {
    return std::strcmp(s1, s2) &lt; 0;
};

答案2

得分: 2

因为在这行代码中 vector vec2 = {&quot;sam&quot;, &quot;zach&quot;, &quot;adam&quot;, &quot;peter&quot;},你没有指定向量元素的类型,而类型是从初始化程序中推断出来的。由于你使用了字符串字面值 (const char[]),因此 vec2 的类型被推断为 std::vector&lt;const char*&gt; 而不是 std::vector&lt;std::string&gt;

然后,在你定义 auto cmp = [](const auto&amp; s1, const auto&amp; s2) 时,你再次要求编译器推断 s1 和 s2 的类型。通过在 std::sort 中使用 lambda 表达式,类型被推断为 std::vector&lt;const char*&gt;value_type,因此你实际上正在比较两个 const char*,这是指针比较,比较不相关的指针是未定义行为,意味着可以发生任何事情。

当你使用形式 [](const std::string&amp; s1, const std::string&amp; s2) 时,向量中存储的 const char* 被转换为临时的 std::string,然后进行比较,两个 std::string 之间的比较是定义良好的,可以得到预期的结果。

顺便提一下,不要使用 using namespace std,特别是如果你刚开始学习 C++。(https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice)

英文:

That is because in the line vector vec2 = {&quot;sam&quot;, &quot;zach&quot;, &quot;adam&quot;, &quot;peter&quot;}, you didn't specify the type of the element of the vector, and the type is deduced from the initializer. Since you used string literals (const char[]), the type of vec2 is deduced to be std::vector&lt;const char*&gt; instead of std::vector&lt;std::string&gt;.

Then later when you define auto cmp = [](const auto&amp; s1, const auto&amp; s2), you are asking the compiler to deduce the types of s1 and s2 again. By using the lambda in std::sort, the type is deduced to the value_type of std::vector&lt;const char*&gt;, so you are really comparing two const char*, which are pointer comparisons and comparison of unrelated pointers are undefined behavior, meaning anything can happen.

When you use the form [](const std::string&amp; s1, const std::string&amp; s2), the const char* stored in the vector are converted to temporary std::strings before comparing, and the comparison between two std::strings are well-defined, giving the expected result.

As a side note, do not use using namespace std especially if you are just starting to learn C++.(https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice)

huangapple
  • 本文由 发表于 2023年7月10日 17:51:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76652581.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定