Hacker News new | ask | show | jobs
by kllrnohj 2309 days ago
> So is this C++03 /C++11 stuff just updates to this library, or is std::string recognised at the compiler level?

std::string is in the STL. So is std::string_view. There are language changes as well that enable some of the new STL additions but some are just STL updates and you could "backport" them so to speak. Or do the same thing but yourself.

Making std:: verboten these days would likely be a mistake, though. There's so many things that are just not controversial and not worth re-implementing. Like std::unique_ptr. Or std::array.

Most of the containers are still better off ignored, though, many of them unfixable. The least worst of them is std::vector and it's still _ok_ but even there things like absl::InlinedVector are worth a strong consideration instead ( https://github.com/abseil/abseil-cpp/blob/master/absl/contai... ). Or boost's small_vector ( https://www.boost.org/doc/libs/1_60_0/doc/html/boost/contain... )

1 comments

I still think the STL containers are the sane default to reach for & switch to more obscure ones when the problem domain and performance requirements say to use a different one.
You have that backwards. The STL containers are for when you have a hyper-specific niche use case. They are otherwise terrible defaults and everyone should use boost or abseil by default otherwise.

std::map, for example, is only appropriate if you need a red-black tree specifically. Which almost nobody does. std::unordered_map is less awful, but abseil has a literal straight upgrade. With the same API. So... why would you pick the slower thing when you're using C++? std::vector is only really appropriate if you know you never have small vectors, which is again a more obscure situation.

> You have that backwards. The STL containers are for when you have a hyper-specific niche use case.

That assertion makes no sense at all. The stl contrainers work very well as basic generic containers that can safely be used for pretty much any conceivable use where performance isn't super critical. I'm talking about cases like, say, you need to map keys to values but you don't really care about performance or which specific data structure you're using. That's stl's domain: robust, bullet-proof implementations of basic data structures that are good enough for most (or practically all) cases with the exception of a few very niche applications.

If you happen to be one of the rare cases where you feel you need to know if a container is built around a red-black tree or any other fancy arcane data structure, and if this so critical to you that you feel the need to benchmark the performance to assess whether you either need to use non-defaults or completely replace parts or the whole container with a third-party alternative... Then and only then the stl is not for you.

This makes no sense. The STL is the specialized containers with obscure performance characteristics & behaviors. Boost & abseil provide the generic, reasonable default ones.

You're arguing it's better to use something that's across the board worse for nearly every user, and by a lot, just because... why? It's slightly more convenient?

> This makes no sense. The STL is the specialized containers

It really isn't. The whole STL is, by design, a template library packed with generic data structures that are designed to have very robust defaults and still be customizable and extensible.

When the defaults are good enough, which is the case in general, the STL will do. If you have a niche requirement (say, games) or feel adventurous, you adopt custom and/or specialized solutions.

This has been the case since the STL's inception. They are the standard, default library. I can't understand how someone is able to miss this fact.

Because STL is part of the compiler, guaranteed to work on every platform supported by the compiler, and does not require lots of paperwork for adoption at many shops.
> Because STL is part of the compiler, guaranteed to work on every platform supported by the compiler

No it isn't and no it's not. There are even platforms where an STL isn't even provided out of the box, you have to pick one. And there's quite a few at that - libc++, libstdc++, stlport, etc...

But clang, g++, etc... they don't care. To them it's just another library you're linking against, no different from any other dependency. They don't provide it, they don't care. It can even be quite a pain in the ass to use the "native" STL of a given compiler, like trying to use libc++ with Clang on most Linux distros.

Small vectors break iterator guarantees, for one thing. They also really only make sense for tiny objects (ints, etc.) given you don't want a pickup truck's worth of data on your stack. They're most definitely not general-purpose.

There are lots of subtleties STL containers have to worry about in designing containers, regarding everything from iterator & pointer invalidation to allocation and allocator propagation. All this is because they're designed to be general-purpose and support most conceivable use cases. Their replacements have to trade off requirements in order to get better performance or otherwise improve on some axes.

> Small vectors break iterator guarantees, for one thing.

It only breaks swap of the container itself during iteration. Which is a super niche condition.

And that swap also invalidates some of std::vector's iterators as well - specifically the end() iterator.

> They also really only make sense for tiny objects (ints, etc.) given you don't want a pickup truck's worth of data on your stack. They're most definitely not general-purpose.

Of course they are still general-purpose. They can (and do) specialize on the size of the object being contained. The only reason std::vector doesn't also have SSO is because it's an ABI break. Not because it's better in some way or less fragile. Legacy is the only reason.

> And that swap also invalidates some of std::vector's iterators as well - specifically the end() iterator.

And they don't invalidate the iterators that point to actual elements, which was kind of the entire point I was making. Don't let that stop you from trying to make it look like I'm just blurting out nonsense, though.

You made a broad, vague claim that iterator guarantees were broken. You misrepresented it as being a much larger issue that it actually is. Nearly all iterator guarantees are not broken. One very specific guarantee in one very specific case is, that's it. And it's a rare, not general, case at that, making the trade-off necessary to achieve it a bad default.
how do small vectors break iterator guarantees?
By invalidating iterators (and pointers and references) when swapping: https://stackoverflow.com/a/8191356
Not sure about iterators, but they do break reference guarantees. Moving a vector doesn't invalidate pointers to the contained elements.
Because that way you don't need to haul in dependencies unless you have a real reason.

std::function is fine for prototyping, but its size hit is extreme, so in embedded code we use other implementations. But where size and speed doesn't matter? Why bother?

> Because that way you don't need to haul in dependencies unless you have a real reason.

These are all largely header libraries. You're already hauling in a dependency, and in every c++ file that uses it at that.

> std::function is fine for prototyping

std::function isn't part of the containers library of the STL (containers being all the stuff here: https://en.cppreference.com/w/cpp/container ). I agree std::function is fine, it even has a pretty reasonable small-size optimization.

> These are all largely header libraries.

That's not even true for boost, no matter if they always advertise that. The lib is also notorious for bad decomposability (using only a subset without installing the whole monster). Not to speak about idiosyncratic naming and build system, making it sometimes hard to include it in meta builds of other libraries and frameworks.

In sum: Anyone sensible, regarding different kinds of footprint and dependencies will think twice, before pulling in these kind of libraries.

boost does not advertise being an header only library. Some boost libraries are header only, some offer hybrid modes, some require a library to be linked in. Each case is documented individually.

Boost is better thought as a loose collection of libraries that try to follow some common design principles instead of a single monolithic library.

I can't defend the build system though.

I'll use java where size and speed doesn't matter.
I rather have my phone be fast, without the apps taking ages to download.
Java apps are actually pretty small in size. It's the assets that make take up the most space, and that is independent of language.

App installation is a one time cost anyways. Did you mean s/download/startup/?

abseil's might have a similar API but it's most definitely not the same (function signatures looking the same doesn't make it the same API). Some of the standard containers can't be fast because too strict/specific standard requirements, not because they don't try hard enough.

Having said that I think using abseil's containers is reasonable, even as a default, if you can afford the dependency.

> std::unordered_map

AFAIK unordered_map is the most awful of all standard containers.

what's wrong with unordered_map? it's at least more useful than map.
From what I understand its API is overspecified to the point that it basically forces a "traditional" array-of-linked-buckets implementation, which can be horribly slow on modern processors due to the need to chase pointers. This means a lot of the potential performance improvements of allowing map elements to be unordered are lost.
Because C++ is a nice, mostly safe, general purpose language, being spoiled by the 1% "performance above anything else crowd".

I want my OWL, VCL back, not an hash table able to do lookups in micro-seconds

I know you're exaggerating here a bit, but come on, a hash table not able to do lookups in micro-seconds is just garbage. I expect better of any language, not just C++.
Indeed I was.

The point I was trying to make, was that from productivity point of view, there are more relevant stuff to fix in C++ than algorithm complexity of STL implementations.

Like catching up with what Java 5 standard library offers for networking and parallel/concurrent programming.

on the contrary, std::map is a good default container with predictable performance. If you need fast O(1) look up std::unordered_map is really not fit for purpose and requires you to come up with an hash function.
> on the contrary, std::map is a good default container

If you care about performance then it's not. (And if you don't care about performance then why are you using C++?) The standard requires that `insert` will not invalidate iterators, which basically forces everyone to implement `std::map` as a red-black tree, and those are pretty bad performance-wise on modern hardware mostly due to cache misses.

> If you need fast O(1) look up std::unordered_map is really not fit for purpose and requires you to come up with an hash function.

Modern hash table implementations (along with a modern hash function) are exactly what you should use if you need fast average-case O(1) lookup, so I'm confused why would you say that it's not fit for purpose? Unless you specifically meant only `std::unordered_map` which, yes, is pretty atrocious performance-wise (again, due to the iterator invalidation requirements).

I meant specifically std::unordered_map, because how it is specified in the standard it is very hard to implement it efficiently. If you need performance, yes, use a good hash table implementation. But even in C++ you do not need to be shaving cycles everywhere and there std::map is better than std::unordered_map.