On 27.04.2021 14:07, Juha Nieminen wrote:
> [snip]
>
> Indeed, std::string makes such things so much easier and simpler.
> On the flipside, perhaps it makes things a bit *too* easy, as it
> induces people to create inefficient code with it, perhaps due to
> ignorance or laziness.
>
> For example, I am currently dealing with C++ code that does a lot of
> things like:
>
> if(str1.substr(n, 5) == str2)
>
> Mind you, this is in commercial production code done by a relatively
> experienced C++ programmer, not some noob.
>
> I'm constantly thinking like "sheesh, there's already a function that
> does that exact comparison in-place, without creating any temporary
> copies, avoiding a useless allocation, copying of data and deallocation."
> Namely:
>
> if(str1.compare(n, 5, str2) == 0)
>
> In this particular case it's not like the more efficient version is more
> complicated to write or use, or would require several lines of code,
> or something.
The rewrite has many more degrees of freedom. I couldn't write that
without checking the documentation of `compare`. Of course, that
checking might be nothing more than inspecting an editor's tooltip, but.
Also, the first version expresses nearly directly the desired operation,
while the rewrite is just that: a rewrite to fit more arbitrary
requirements, in the direction of expressing C++ code in assembly.
As I see it the second version is therefore decidedly less clear to a
good C++ programmer (even a C++20 programmer accustomed to `<=>`), so
that it can waste people's time when they have to deal with the code,
but since it uses the three-value idiom it may be slightly more clear to
a C programmer accustomed to `strcmp`; at least, to an associative C
programmer, and which programmer doesn't delegate the small stuff to the
brain's automatic associative machinery, with "== 0" there's something
familiar, while "== str2" could look like a bug of comparing pointers.
The efficiency or not of a single instance of possible dynamic
allocation is of little concern unless /measurements/ say that this, or
the wholesale adoption of this as a practice, is a problem.
However, by coding for efficiency /by default/, when that's possible
without sacrificing clarity, one is likely to avoid Microsoft-like
sluggishness and extreme inefficiency. Like half a minute to show a
right-click popup menu, or so sluggish directory operations that high
quality programs give up (ordinary programs just slow down with
inexplicable pauses). In modern post-C++17 one can do
if( string_view( str1 ).substr(n, 5) == str2 )
Disclaimer: I didn't compile that.
And of course, if that's needed three times or more one could/should
define a function `substr_view`.
> There are lots and lots of other examples, related to std::string
> (and std::wstring), such as using it for fixed-sized strings in situations
> where a small inbuilt char array would suffice, always taking a std::string
> as parameter when a const char* would suffice (or could well be
> provided as a more efficient alternative) and so on and so forth.
I agree that the standard library support is currently very much less
than complete for supporting basic programming.
In the positive direction, for `std::string` one can use
`std::string_view` in a great many cases, in particular for parameters,
but also for e.g. your string comparison example.
Using `std::string_view` as parameter type when that's practically
possible, should be one's default.
But when the parameter is to be used as a null-terminated C string then
that introduces inefficient copying in all cases. That gross
inefficiency can be avoided with /a parameter type that represents a
guaranteed nullterminated string/, i.e., by not just trowing away that
information about the actual argument. Then the copying is only needed
when the actual argument is a `std::string_view`, and furthermore it can
then be centralized and hidden away in the parameter type.
That's one glaring omission in the standard library.
A guaranteed non-throwing string carrier is sometimes needed, e.g. as
part of an exception value. The standard library /uses/ such a type
internally, it's not just an implementation detail but absolutely
required since standard exceptions must be copyable without throwing,
but that internal type is not exposed. One can implement such a type
trivially, however, by leveraging e.g. a `std::runtime_error` as string
carrier.
So, that's another glaring omission in the standard library.
> std::string is an awesome tool that makes one's life a thousand times
> easier, but any competent C++ programmer should learn how to use it
> *efficiently*, not just lazily. Know your tools. Use them efficiently.
Yep.
- Alf