为什么 std::max 对字符串字面值无效?

5

我试图找到两个字符串中的最大值,在第一个情况下(当传递 std :: string 变量时)它给出了正确的答案,但在第二种情况下(当直接传递字符串时)却出现错误。

#include<bits/stdc++.h>
using namespace std;

int main()
{
    // Case 1
    string str1 = "abc", str2 = "abcd";
    cout << max(str1, str2) << endl;

    // Case 2
    cout << max("abc", "abcd") << endl;
}

5
必须提供一个链接到为什么不应该#include <bits/stdc++.h>?。但是如果您仍然使用它,并将其与using namespace std;结合使用,事情可能会变得非常奇怪 - user4581301
你是想找到最长的字符串还是按字典顺序排列后的最后一个字符串? - Alan Birtles
2个回答

10

在你的第二种情况下,

std::cout << std::max("abc", "abcd") << std::endl;

它们是字符串字面值,其中"abc"的类型为char const [4]"abcd"的类型为char const [5]

因此,在函数调用std::max("abc", "abcd")中,std::max必须推导出

auto max(char const (&a)[4], char const (&b)[5]) {
    return a < b ? b : a;
}

由于std::max没有接受不同类型的函数模板重载,因此这是不可能的。所以出现了错误!


警告!

如果在std::max中显式地提到模板类型const char*,那么它就可以被编译。 这是因为对于"abc""abcd",类型也可以是const char*,这是由于C++中的数组指针衰减导致的。

 std::cout << std::max<const char*>("abc", "abcd" ) << '\n';  // compiles
                      ^^^^^^^^^^^^^
此外,std::maxstd::initializer_list重载将推断上述内容也作为模板类型,并且将其视为const char*
std::cout << std::max({ "abc", "abcd" }) << '\n';   // compiles

然而,你不应该这样做

正如@AlanBirtles所指出的那样,这可能会导致未定义行为,因为std::max将比较两个不同数组的指针。结果不能被中继并且应该执行上述操作。在比较中使用std::string,就像你的第一个例子一样。使用字符串字面值(自C++14以来),您可以进行最小更改,并使第二个案例与第一个案例相同:

#include <string>
using namespace std::string_literals;

std::cout << std::max("abc"s, "abcd"s) << '\n';

顺便提一下,请参阅以下内容:


5
使用const char*的变量进行比较可能会编译通过,但是将两个不同数组的指针进行比较具有未定义的行为。通常来说,它只会返回存储在内存中的第二个字面量,这不太可能是原帖的意图,并且与使用std::string的行为不同。 - Alan Birtles
1
这个输出可能很出乎意料:https://godbolt.org/z/6E1odqT4a - Alan Birtles

0

区别在于类型。

典型的最大值实现可能如下所示:

template <typename T>
auto max(const T &a, const T &b) {
    return a < b ? b : a;       
}

当你使用 std::string 的 max 函数时,< 运算符实际上被重载了。会使用 std::string::operator<() 方法来比较字符串。
当你使用 const char * 的 max 函数时,< 只是比较指针,而不考虑字符串的内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接