5

I am trying to find the maximum of two strings and it is giving me the correct answer in the first case (when passing std::string variables) but giving an error in the second case (when passing direct strings).

#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;
}
Boann
  • 48,794
  • 16
  • 117
  • 146
  • 5
    Obligatory link to [Why should I not #include ?](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h). But if you use it anyway and combine it with `using namespace std;`, [things can get really weird](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice). – user4581301 Sep 10 '21 at 06:37
  • Are you trying to find the longest string or the later one in lexicographic order? – Alan Birtles Sep 10 '21 at 07:00

2 Answers2

10

In your second case,

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

they are string literals, in which "abc" has type char const [4] and "abcd" has type char const [5].

Therefore, in the function call std::max("abc", "abcd"), the std::max has to deduce

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

This is not possible as the std::max has no function template overload, which takes different types as template arguments. Hence, the error!


Warning!

If you explicitly mention the template type const char* in std::max, this could have been compiled. This is because, for "abc" and "abcd" the type can be also be const char*s due to array to pointer decay in C++.

 std::cout << std::max<const char*>("abc", "abcd" ) << '\n';  // compiles
                      ^^^^^^^^^^^^^

In addition, the std::initializer_list overload of the std::max, in turn will deduce the above also const char* as template type:

std::cout << std::max({ "abc", "abcd" }) << '\n';   // compiles

However, you should not be doing it !

As @AlanBirtles pointed out, this can cause the undefined behavior, due to the fact that the std::max will compare the pointers of the two different arrays. The result can not be relayed and should be doing the above. Use the std::string for comparison as in your first case. Using string literals (since C++14), you can do a minimal change, and make the second case, same as first one:

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

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

As a side note, see the followings:

JeJo
  • 30,635
  • 6
  • 49
  • 88
  • 5
    Whilst the `const char*` variants might compile comparing the pointers from two separate arrays has undefined behaviour, it's also likely to just return whichever literal is stored second in memory which is not likely to be what the OP wanted and is certainly not the same behaviour as with `std::string`s – Alan Birtles Sep 10 '21 at 06:58
  • 1
    e.g this output might be surprising: https://godbolt.org/z/6E1odqT4a – Alan Birtles Sep 10 '21 at 08:27
0

The difference comes from the types.

Typical max implementation may look like this:

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

When you use max for std::string that < sign is actually overloaded. std::string::operator<() method is used to compare strings.

When you use max for const char *. < just compares pointers disregarding content of the strings.

Nolan
  • 1,060
  • 1
  • 11
  • 34