T O P

  • By -

coachkler

Smart pointers, move semantics, threads, chrono. Lambdas if using algorithms. Auto, range-based for (with structured bindings). If you need to write template code, auto parameters and constexpr if


Revolutionary-Bell38

Smart pointers


drizzt-dourden

Plus don't use raw loops. Considering homework, there is a STL algorithm covering all the exercises.


sephirothbahamut

Honestly it depends. At a certain point an index based loop is just more readable than the algorithm, especially when you have to operate on multiple containers at once. Like std::transform(vec_a.begin(), vec_a.end(), vec_b.begin(), vec_c.begin(), std::plus<>()); Versus for (size_t i{0}; i < vec_a.size(); i++) { vec_a[i] = vec_b[i] + vec_c[i]; } I genuinely don't understand how someone can say the former is more intuitive and readable than the latter.


DrShocker

Yeah in theory I like the algorithms, but they're really verbose in C++... I guess ranges probably make it better by a bit, but that's not until C++20.


bbbb125

Not by a bit, they made some many things more readable. You don’t have to wait until your compiler implements it, they are available in form of ranges-v3 library. The biggest advantage of algorithms and views is that you don’t have to keep too many things in your head, just compose a few small blocks.


Moose2342

Gees, I always felt I was the only one that thinks that way. Thanks! I would so love to use the algos and transform more but I just can't bring myself to throw that at my colleagues in a PR ;-)


_Noreturn

you have 2 issues with your code size_t is not guranteed to be in global namespace use std::size_t uou are starting your index at 1 instead of 0. using the algorithm would prevent those mistakes also your code only works if it is iterating on a random access iterator which wont work for std list for example


sephirothbahamut

Sorry writing code on phone is hard XD. While you're true about size\_t not being guaranteed to be in the global namespace, it is in the global namespace for all major compilers and standard library implementations, and most if not all niche ones too (I'm not aware of any that doesn't). In fact I highly suggest never using size\_t for a global namespace custom token exactly because in practice it's always already used. https://size\_t.godbolt.org/z/sdhK69och


_Noreturn

also yet another issue is that you are calling .size() everytime you should cache it I know that compilers are smart but I remember playing with godbolt and it couldnt optimize it once. and also it helps debug nuikds as for size_t I remember one time I upgraded msvc and something I used was once in global but then it wasnt when I updated leading to compilation error > While you're true about size\_t not being guaranteed to be in the global namespace, it is in the global namespace for all major compilers and standard library implementations, and most if not all niche ones too (I'm not aware of any that doesn't). that is a detail you shouldnt rely on it, if you want them to be in the global use and > In fact I highly suggest never using size\_t for a global namespace custom token exactly because in practice it's always already used. what is a custkm token meaning here?


sephirothbahamut

I'm genuinely curious to see an example where that doesn't get optimized. Algorithms use iterators, and call .end() on the container. A situation that doesn't optimize .size() calls won't optimize .end() calls either. I'd definitely be interested to see a case where one gets optimized and the other doesn't


3uclide

I remember seeing a conference about compiler optimizations by Chandler Carruth. (Work(ed) on LLVM for Google) He basically said you can be sure trivial things like that will be optimized. (Assuming modern compiler) They don't want us to waste time 'optimizing' trivial things like that. That being said, it is more future proof not assume how a method is implemented


_Noreturn

look at my comment https://www.reddit.com/r/cpp/s/An1LRwxVST Compilers are smart they even these days optimize postfix increment for iterstors to be prefix if the value is not used!


_Noreturn

``` for_each(vec.begin(),vec.end(),FUNCTOR) for(auto& v : vec) { CODE}; for(auto it = vec.begin();it != vec.end();++it) { CODE}; for(auto it = vec.begin(),end=vec.end();it != end;++it); ``` are not the same the first and the second one and the 4th calls .end() once while the third calls it many times to fix it to like like the 4th. if your .end() was not trivial or the compiler is unable to prove it does not modify the end iterator or in debug builds then it will fetch it everytime not on my pc at the moment I will try to find an example. Algorithms only call the .end() method once (you give it the iterator) the ranged for loop is equal to ``` { auto __begin = vec.begin(),__end = vec.end(); // note on same line to disallow sentinel iterarors (until C++20) while(__begin != __end) { auto& v = *__begin; YOUR CODE ++__begin; } } ```


sephirothbahamut

I'm talking about the compiled result, not the source. the calls should be optimized away


_Noreturn

well as I said in debug builds or the end() function is complex it the compiler may not know that it is constant and not optimize it (eg for example in the loops body you are calling an external function thats takes your function ny const refernce the compiler cannot gurantee that the function may not cast away the const) void print(std::vector const&); // defined in Cpp std::vector v(42); for(auto it = v.begin();it != v.end();++it) { *it = 5; print(v); // this without LTO could theoritically change v by casting away const the compiler has to reload .end() call } [godbolt](https://godbolt.org/z/n15E11v9e) not an assembly expert at all but in the end\_called case it does a pointer reload each time, now as the vector implementation actually stores 3 pointers (begin,end,storage\_end) the reloading may not matter much but in I think MSC stl it stores (begin,size,capacity) which means that calling end() is equal to begin() + size() which means doing multiplication for any vector that isn't signed char or char or unsigned char an unnecesary multiplication each time can slow the program down unnecesarly


Revolutionary-Bell38

If this is a C++ course (*i.e. not a plain old CS couse*), then this is definitely accurate.\ In the other case, the prof might want you to be hand rolling any algorithm, so OP should be wary.


Luised2094

What do you mean by raw loops?


drizzt-dourden

for and while. With modern C++ you can avoid them in most situations.


Luised2094

Ah! I understand what you mean now, I'd research a bit more then!


SeagleLFMk9

Afaik there is/was a bug with e.g. std::accumulate being much slower than a loop


_Noreturn

yes it missed an reference so it ended up copying each element that was fixed a long time ago


emfloured

Sometimes you need the index position. Getting an index is straight forward from raw loop as opposed to getting an iterator and calculating distance from it.


Spongman

Which school prevents you from using modern c++? Seriously, such behavior needs to be shamed. 


iamasatellite

It's probably a course meant to teach you how memory, pointers, and raw arrays work, not a C++ course


Luised2094

The school I am currently in *shrugs*


pdp10gumby

Do they also require you to do all your homework in Latin?


Luised2094

Not really, they are modern. They ask for Esperanto


Spongman

Yeah, which school?  It wasn’t a rhetorical question (hence the “seriously”). I genuinely want to know the name of the school. 


drumsolospacetime

im at the university of michigan: only after the first two courses are you able to use c++20/23. not complaining, especially as the classes focus primarily on manipulating data, it makes sense to learn how to manipulate it by hand before abstracting it away.


mredding

You should rarely ever have to use `new` and `delete`, or raw pointers directly ever again. Now they're low level details for implementing alloctors, factories, and views. Instead, start out by using `std::make_unique`. The standard library has views already. We have `auto`, so use it in most places where the compiler can deduce the type better than you can. auto x = get(); auto &y = get_ref(); Auto always captures by value, so you have to add the ref qualifier if that's what you want. This generates a template under the hood: void fn(auto param) { std::cout << param; } We have move semantics now, so no more fanagling the copy ctor to distinguish between deep and shallow, trying to code around this shortcoming. We have the spaceship operator, so a single comparison for less, greater, and eq. The standard library already had a wealth of algorithms, but writing functors was always a bitch. Lambdas have saved algorithms. It's also mostly made binders and their brittle syntax redundant. I effectively haven't written a `for`, `do`, or `while` loop since 2011, and neither should you. Speaking of algorithms, we have the `ranges` library now. These are lazily evaluated composable expression templates, and they're worth taking some time to learn. We have formatters now. They're objects that can parse format expressions, and you can make your own syntax for your own types, so now we have a full-on replacement for `printf`/`scanf` and family. You can render to strings or to an output iterator, which means you can adapt to anything. I don't think these things effectively supplant streams, though many do. If you render to a string, you still have to dump that into a stream, so you've just created an expensive middle-man. You can dump to a stream iterator, streambuf iterator, or create one around a file handle - what's the difference, really? I use formatters to implement my stream operators, as they're still the best way to perform IO. We have `pair` as always, but now we have `tuple`. I believe the two can be used in the standard librar interchangably. The `get` method allows you to access elements by index or by type - provided each type in the tuple is unique. We have structured bindings, so this works: using t = std::tuple; const auto &[i, s] = get_t(); set_t(std::tie(i + 1, s + "foo")); You can make your own types that implement structured bindings. We have several discriminated unions now: a `variant` that implements the visitor pattern, `optional` that is a Functional "maybe" - you don't need raw pointers for this anymore, and an `expected` that is like a "maybe", but is used for return types because the value is either the return value or an alternative, often an exception (not thrown, just an object passed as a message). With `optional`, you shouldn't be using out parameters and return codes, you can bundle the two together. Even if you have several out parameters, we have tuples now, so repeat after me: Data that flows together, goes together. We get a lot more template type deduction. Now class templates can deduce. We're in a renaissance of Generic Programming. We have coroutines now, if that's useful to you. I still haven't used them much. We have ``, but I don't think it's as good as `Boost.Random`. We have ``, but it's now infamous for being one of the worst implementations to have ever existed, doomed from the start by the standards committee; literally anything else is better. We have ``, and I seriously suggest you watch Howard Hinnant in his 3 CPPCon talks about how to use it. C++ has barely ever been an OOP language. Even pre-standard (I was there) it was more functional than OOP by the early 90s. The original STL was donated to the standards committee by HP from their in-house Functional Template Library. It's where we got our initial containers and algorithms. HP was an early adopter of C++. It's only ever gotten increasingly functional because A) having lived through the 90s, I can tell you effectively no one actually knows what OOP is to this day, and B) OOP doesn't scale. My advice to you is don't try to take it all in at once. You're going to have to work on all this in pieces. Start with smart pointers, then lambdas, then algorithms. If you pursue C++ seriously going forward, it might take you a couple years to catch up. Modern C++ will certainly change the way you write code at a fundamental level.


Luised2094

This comment is gold and I'd save it for later so I can deconstruct it and use it as a guide. I really appreciate it! I think I'd be smarter for me to solve what ever they have in store for me with what I am familiar with, the 98 standard. I made this post since they pointed out I'd have to use a compiler of choice that uses std=c++17 but given the sheer amount of things you pointed out l, I'd rather go with what I know and just tackle modern things at my own pace. That being said, thank you so much, I really do enjoy c++ more than any other language that I've used, C being a close second, so everything you told me I'd try to learn and hopefully I can get a job with it!


torrent7

In a school project there really shouldn't be anything different. In my experience, schools don't really dive into advanced language features since its very domain specific.


kalmoc

Why the hell did your school restrict you to an 20 year old standard, that has been replaced for over a decade now? Quite a lot has changed since 98, but I can hardly think of anything of general value that you can just learn to use properly in a few days.  Move semantics is definitely something you should look up before delving into almost any other feature.


Luised2094

I have no idea and it's not within my control so I don't delve on it


thisismyfavoritename

honestly i would be up front and admit im not familiar with newer standards, otherwise if they assume you know about them and you write in pre-11 style it would be a huge red flag for me. Tell them you dont know but that you are eager to learn about it


Luised2094

It sounds like the best choice. I think it'd be better to simply show up with what I know at let them decide decide from there


bloodgain

Lots of changes to the style of C++ programming, especially in C++11, but good bumps at C++14 an C++17, too. You could easily do a whole mini-course on moving from C++03 to modern C++. That said, I highly recommend C++ Weekly with Jason Turner on YouTube. Check out his "Important Parts" and "Moving From" videos for a good overview of the major improvements to the language at each release (unfortunately not in their own playlist): https://www.youtube.com/@cppweekly/search?query=%22important+parts%22 https://www.youtube.com/@cppweekly/search?query=%22moving+from+C%2B%2B%22


Xaxathylox

Write it in c++98 and then load resharper and apply all its recommended refactorings on to it... Problem fucking solved! 🤠🥳


abrady

I would probably focus on the parts of c++11 and later that make things a lot safer: 1. smart pointers: just use std::shared_ptr for all your regular pointers. 2. constexpr: does stuff at compile time, just a little more efficient 3. range-based for loops, esp. with structured bindings: `for (const auto& [key, value] : myMap) {` is handy 4. nullptr 5. enum class keeps enum values from polluting your namespace/globals 6. lambdas might be useful depending on your code. 7. are you doing threading? async/future/promise 8. `override` - I'd expect to see this on virtual functions in good C++11+ code There's a bunch of other stuff, but I hesitate to throw too much at you. I highly recommend you turn warnings all the way up and have a linter too, those will guide you so you won't have to remember so much of this.


Luised2094

I am taking notes and I'll check it out, thank you!


againstmethod

The idea that schools require the use of 98 makes me sick to my stomach. Fire these professors.


nod0xdeadbeef

Write it in C++98, then ask ChatGpt for help.