My first php script was a file upload server for a lan party. Luckily nobody tried to upload a file named ../index.php, because I realized afterwards that it would have worked :p
And I see this argument often. People make too much fuss about the massive error messages. Just ignore everything but the first 10 lines and 99.9% of the time, the issue is obvious. People really exaggerate the amout of time and effort you spend dealing with these error messages. They look dramatic so they're very memeable, but it's really not a big deal. The percentage of hours I've spent deciphering difficult cpp error messages in my career is a rounding error.
Do you also consider that knowing type deduction is not necessary to fix those errors, unless you are writing a library? Because that is not my experience (c++ "career" can involve such wildly different codebases, it's hard to imagine what others must be dealing with)
v is double& in your example, not double. But it's not obvious that omitting the & causes a copy. If you see "for (auto v : vec)" looks good right? But if vec contains eg long strings, you've now murdered your perf because you're copying them out of the array instead of grabbing refs. Yes, you could make the same mistake without auto, but it's easier to notice. It's easy to forget (or not notice) that auto will not resolve to a reference in this case, because using a reference is "obviously what I want here", and the name of the feature is "auto" after all - "surely it will figure it out, right?"
Wow I really thought this would be a compile error. The implicit cast here really is a footgun. Looks like '-Wrange-loop-construct' (included in -Wall) does catch it:
> warning: loop variable 'v' of type 'const std::pair<std::__cxx11::basic_string<char>, int>&' binds to a temporary constructed from type 'std::pair<const std::__cxx11::basic_string<char>, int>' [-Wrange-loop-construct]
11 | for (const std::pair<std::string, int>& v: m) {
Why is this a perf footgun? As someone who doesn't write a lot of c++, I don't see anything intuitively wrong.
Is it that iterating over map yields something other than `std::pair`, but which can be converted to `std::pair` (with nontrivial cost) and that result is bound by reference?
It is not a cast. std::pair<const std::string, ...> and std::pair<std::string,...> are different types, although there is an implicit conversion. So a temporary is implicitly created and bound to the const reference. So not only there is a copy, you have a reference to an object that is destroyed at end of scope when you might expect it to live further.
I guess this is one of the reasons, why I don't use C++. Temporaries is a topic, where C++ on one side and me and C on the other side has had disagreements in the past. Why does changing the type even create another object at all? Why does it allocate? Why doesn't the optimizer use the effective type to optimize that away?
> Why does changing the type even create another object at all?
There's no such thing as "changing the type" in c++.
Function returns an object type A, your variable is of type B, compiler tries to see if there is a conversion of the value of type A to a new value of type B
Each entry in the map will be copied. In C++, const T& is allowed to bind to a temporary object (whose lifetime will be extended). So a new pair is implicitly constructed, and the reference binds to this object.
It's a foot gun. Why is the default target for this gun my foot? "You should be careful to choose the target properly" isn't an answer to that question.
In what way it is a footgun? auto x = ... ; does what I would expect. Copying is usually the default in C++ and that's what I would expect to happen here.
If auto deduced reference types transparently, it would actually be more dangerous.
C++ has value semantics, which means that values of user-defined types generally behave like values of built-in types. In this sense, copying is the only logical default. It's just how the language has been designed.
Things are different in Rust because of lifetimes and destructive moves. In this context, copying would be a bad default indeed.
> because who said that's even achievable, let alone cheap?
Nobody said that. The thing is that user-defined types can be anything from tiny and cheap to huge and expensive. A language has to pick one default and be consistent. You can complain one way or the other.
I find passing by value to be sensible, but the allocating part sounds like a bad idea. Passing the value of something doesn't imply making a copy, if the value is never changed, it can be entirely optimized away.
> Passing the value of something doesn't imply making a copy
Yes, languages like Rust can automatically move variables if the compiler can prove that they will not be used anymore. Unfortunately, this is not possible in C++, so the user has to move explicitly (with std::move).
> Unfortunately, this is not possible in C++, so the user has to move explicitly (with std::move).
Honestly why not? A locally used variable sounds to be very much something the compiler can reason about. And a variable only declared in a loop, which is destroyed at the end of each iteration and only read from should be able to be optimized away. I don't know Rust, I mostly write C.
Yes, this could work for simple cases, but this breaks down pretty quickly:
void checkFoo(const Foo&);
Foo getFoo();
void example() {
std::vector<Foo> vec;
Foo foo = getFoo();
if (checkFoo(foo)) {
// *We* know that checkFoo() does not store a
// reference to 'foo' but the compiler does not
// know this. Therefore it cannot automatically
// move 'foo' into the std::vector.
vec.push_back(std::move(foo));
}
}
The fundamental problem is that C++ does not track object lifetimes. You would end up with a system where the compiler would move objects only under certain circumstances, which would be very hard to reason about.
Ah, you are right. It theoretically should be able to do it, but even with -flto, there are likely cases, where it doesn't. It is less of a problem with C, since you explicitly tell the compiler, whether you want things to get passed as value or pointer. Also I typically annotate ownership, so it it easy to forget that the compiler doesn't actually knows this. This is a weird limitation, there should be just a parameter attribute to do that.
> What if the function is implemented in a shared library
If it is in the public API/ABI of a shared library, than the calling semantics including lifetime and ownership rules are part of the public interface, so of course the compiler can't just change it. You the programmer are responsible for drawing abstraction boundaries and choosing the interface.
> It works the exact same way in C++, though.
Only if write C in C++. The issue here are references, of which the compiler figures out whether this should work like a value or like a pointer. This doesn't exist in C, there the programmer needs to make up its mind and choose. The whole type conversion by making a copy issue also doesn't exist there, because either the type matches or the compiler throws an error.
Maybe I misunderstood, but I thought that you were arguing that with link-time optimization the compiler could see through cases such as the one I have given. As a counterargument, I brought up functions that are implemented in shared libraries, which are essential a blackbox to the compiler.
> The issue here are references, of which the compiler figures out whether this should work like a value or like a pointer.
I'm not sure I understand. A C++ reference always has reference semantics. Can you give an example?
IMO the biggest barrier is internal mobility. The European silicon valley never happened, because people don't want to move around. The biggest single barrier is language. I'm Irish, and young Irish people often emigrate (way more than in other countries). When I look at where my college classmates ended up, it's mostly America or the UK. We also emigrate a lot to Australia and New Zealand. In other words, we only really emigrate to English speaking countries.
Almost nobody goes to France, Germany, Spain, Italy, etc. The mainstays of the European economy. Let alone central or eastern Europe. But if you're a young talented engineer in the middle of nowhere usa, you can just easily move to the bay area without any issue. That cultural unity IMO is America's biggest strength, and the lack of it is Europe's biggest weakness.
Note: I've lived in Ireland, the Czech Republic, and France, so I know first hand how hard it is to move inside Europe, and I understand why people don't do it.
I checked the about page on 4get.bloat.cat, and within the first paragraph of the "what is this" section, it used the phrase "globohomo bullshit". I dont think these are people I want to support.
I know this was in jest, but had some of Apple’s and IBM’s 1990s plans came to fruition, we could’ve ended up with an operating system capable of running both OS/2 and Taligent (the original planned successor to the classic Mac OS) applications on PowerPC hardware (one of the few parts of the Apple/IBM collaboration that was realized):
"We have realised that boiling the frog this fast will result in it jumping out of the water. Therefore we have slowed down, but remain steadfastly devoted to seeing this frog boiled"
100% true, I've hit the front page of hn on a server with an old i5 (aka consumer hardware, and not even high end) with no cloudflare or similar caching, and had no problems. Computers are fast, and serving static html over https is a solved problem.
reply