std::min with const char *

Seems that std::min doesn't work with const char *
Has anyone encountered this problem as well ?

#include <iostream>
#include <algorithm>

int main()
{
const char *lisa = "Lisa";
const char *anna = "Anna";

std::cout << std::min(lisa, anna);
}

Output: Lisa
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <algorithm>
#include <cstring>

int main()
{
   const char *lisa = "Lisa";
   const char *anna = "Anna";

   std::cout << std::min(lisa, anna) << '\n';            // probably undefined behaviour
   std::cout << std::min(lisa, anna,[]( const char*a, const char*b){ return strcmp( a, b ) < 0; } ) << '\n';  // <======
}


Lisa
Anna
Last edited on
When working with char pointers, you need to provide an appropriate comparison function. For pointers, the default comparison used compares the values of the pointers, not the content to which they point.

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <algorithm>
#include <cstring>

int main()
{
	const char* const lisa { "Lisa" };
	const char* const anna { "Anna" };

	std::cout << std::min(lisa, anna, [](const char* a, const char* b) { return std::strcmp(a, b) < 0; }) << '\n';
}


or using std::string_view

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <algorithm>
#include <string_view>

int main()
{
	const char* const lisa { "Lisa" };
	const char* const anna { "Anna" };

	std::cout << std::min(lisa, anna, [](std::string_view a, std::string_view b) { return a < b; }) << '\n';
}


both displays:


Anna

Last edited on
Thanks lastchance & seeplus,
your solutions work, but look a bit akward.

Isn't there a way to specialize std::min.
Unfortunately that doesn't compile:
1
2
3
4
5
6
7
8
namespace std
{
    template <>
    const char* min<const char*>(const char *a, const char *b)
    {
        return strcmp(a, b) < 0 ? a : b;
    }
}


error: template-id ‘min’ for ‘const char* std::min(const char*, const char*)’ does not match any template declaration
did you include #include <typeindex> ?
you may also need to put in some sort of _Compare. I am not sure about the syntax there... something like (untested) ?
template <class _Compare>
constexpr char* min<constexpr char*>(const char *a, const char *b,_Compare)
Last edited on
This would compile:

1
2
3
4
5
6
7
8
9
10
11
12
#include <algorithm>
#include <cstring>

namespace std
{
    template <> const char* const& min( const char* const& a, const char* const& b )
    {
        if( a == nullptr ) return a ;
        else if( b == nullptr ) return b ;
        else return std::strcmp(a,b) <= 0 ? a : b ;
    }
}


And engender undefined behaviour (we can't take liberties with namespace std):
Unless otherwise specified, the behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std.
http://eel.is/c++draft/constraints#namespace.std-1

Seems that std::min doesn't work with const char *

It's because the < operator "doesn't work" with const char * (at least not the way you want)
Last edited on
And engender undefined behaviour (we can't take liberties with namespace std):

I see, so only the C++ standard could do it.
Was it ever proposed? There are probably many STL functions that don't work with pointers.
> There are probably many STL functions that don't work with pointers.
> Was it ever proposed?

The standard library provides std::less (which is the default comparison function for most library components, for example std::set<X*>).

A specialization of std::less for any pointer type yields the implementation-defined strict total order, even if the built-in < operator does not.

The implementation-defined strict total order is consistent with the partial order imposed by built-in comparison operators, and consistent among following standard function objects.
https://en.cppreference.com/w/cpp/utility/functional/less


Note that std::less compares pointers, but it would not compare a pair of null-terminated byte strings lexicographically.

C++17 introduced std::string_view; it has lexicographic comparison operators.
https://en.cppreference.com/w/cpp/string/basic_string_view
Last edited on
You can also do:

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <algorithm>
#include <string_view>

int main()
{
	const char* const lisa { "Lisa" };
	const char* const anna { "Anna" };

	std::cout << std::min<std::string_view>(lisa, anna) << '\n';
}


as std::min is templated.
thmm wrote:
Was it ever proposed? There are probably many STL functions that don't work with pointers.

If you had two pointers to element of the same array you could use std::min to get the one with the smaller index so it already has a meaning. It's probably not used like this very often, so it might not break too much code, but having it do something else would be surprising if you ask me.
Last edited on
but why is lisa and anna char* anyhow - and why not std::string/string_view??
but why is lisa and anna char* anyhow - and why not std::string/string_view??


A friend asked me if std::min and std::max would work with const char*. I didn't know so I wrote some code to try it out.
I can imagine some legacy code that uses const char*. Of course string or stringview would avoid all problems.
Stamino wrote:
I still use const char and I'm fine.
With std::min and std::max? Does it work with them?
Never mind - Stamino is a spammer and has been reported.
Last edited on
strcmp still works.
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <cstring>

const char *minstr(const char *a, const char *b) {
    return (std::strcmp(a,b) < 0 ? a : b);
}
int main()
{
    const char *lisa = "Lisa";
    const char *anna = "Anna";
    std::cout << minstr(lisa, anna) << '\n';
}

I can imagine some legacy code that uses const char*.

Yes, old code from before 1998 and some past it for holdouts (there were reasons for a while) and anything interfacing to C or hardware that/software that uses C style string interface (its difficult to send a C++ style string to/from a simple device, eg a NMEA gps spewer would be a mess trying to send that to you in c++)

but such code would not have tried to use std::min for the C-strings unless the code is not only old but borked up and bug ridden.
Back when times were simpler: we had a way to do this, and didn't need a redundant one. Today a common interface is more expected and having special snowflake stuff is frowned upon. Most of the code I ever wrote or used that had C style strings used arrays, never automatic constant pointers, as you can work with an array much more easily -- the const frequently was in the way.

you have everything you need though to make it happen cleanly in modern code. The string class is and can export a C-style string if you need one, and it can accept one in during construction, assignment, and the + operator and possibly other ways that I haven't worried about. So you can use min and still get a C string out if that is needed. There are only a tiny number of things that don't work well with the C++ tools and are a little better in C. I can only think of two of them, and believe me when I say it, I am one of the worst of the 'holdouts' of pre C++98 you are likely to meet. (Goes hand in hand with redundant ways to do stuff + set in my ways, but I have tried hard this last decade to overcome it).

what two? It annoys me that strstr is effectively a boolean and find() is not. And it annoys me that you can't as easily do in place destructive parsing (eg, move the pointer and zero out the end of lines for an example) of input. Minor things that there are other ways to get to the same place, but not as cleanly for either one: find requires an obnoxious constant (::nopos) and destructive parsing can be emulated with streams somewhat, though it is unfriendly about it. I am also not sure that c++ has a great strtok replacement, but that is a lot like the destructive parsing, same idea, and similar ways around it, just not quite the same.
Last edited on
Topic archived. No new replies allowed.