- std::unique_ptr and std::shared_ptr (your code should not have any new/delete). It's easy to write code with clearly defined ownership semantics, and I've yet to create a memory leak.
- Proper memory model so that multithreading has defined semantics.
- Lots of syntactic sugar (auto, range-for, lambdas, structured bindings, if constexpr, etc.).
- More metaprogramming tools/fun.
C++11 alone is a very different world compared to what you are probably used to.
But coding on C++ has become quite fun. Generic lambdas deserve much of the credit.
I cannot begin to describe how huge a thing this was for me. I came to C++-17 (just about) from C++-98 -- a language I hated so much, I vowed I would never, ever write anything in it on purpose.
It is a massively different beast today from what it was, but the biggest thing of all is this change in how you deal with memory. It was like going from a particularly annoying and verbose aberation of C to a particularly fast and expressive version of Python.
YMMV, of course. In fact, it may vary to the point of becoming absolutely unrecognizable if you're dealing with a massive legacy codebase. But I have to say, I've become a very big fan in the last few years.
Do you actually find it verbose to read or also verbose to type? I don't consider the latter an issue since 1. you normally spend much more time thinking than typing while programming, and 2. code is read much more often than it is written.
More important in my opinion are changes to the language, like lambdas or auto.
I did the latter for a project that started around 2000 and never looked back.
* Hash maps in the standard library (every so often I have to go and look it up to remind myself that no, I’m not imagining things, C++03 really didn’t have hash maps in the standard library, as ludicrous as that sounds)
* Regexes
* Option and Variant types
* Move semantics
And my personal favorite...
* foo<bar<quux>> is no longer a syntax error
But C++ is a systems programming language and * is still quite useful.
std::unique_ptr<foo> m_foo;
How do you pass that into a function that can receive a pointer to foo, but does not require it? The only way to do so is to declare the function to take a foo* and pass it m_foo.get()In summary, * will never be deprecated until C++ has support for something along the lines of Rust's borrowing and lifetimes.
Same as you would have a function that can receive an `int` but does not require it. Or a receive a `std::vector` but does not require it.
I surmise that you mean "how do you pass any value (pointer or otherwise) into a function that can receive a value but does require it". And I surmise you have before used pointer indirection to pass that value because it has a conventional sentinel value of 0/NULL/nullptr.
You are correct in that optional values did not have a standardized solution, until C++17 std::optional.
If you don't have C++17, I recommend one of the equivalent third-party implementations:
* https://www.boost.org/doc/libs/1_52_0/libs/optional/doc/html...
In any case, you can use std::optional<std::reference_wrapper<MyVeryLargeType>>.
I would frankly give a negative code review to anyone who would do that.
You go from e.g.
void my_function(T* foo, T* bar, int baz)
{
// ...
foo->stuff(bar, baz);
}
to void my_function(std::optional<std::reference_wrapper<T>> foo, std::optional<std::reference_wrapper<T>> bar, int baz)
{
// ...
foo->get().call(&bar->get(), z);
}
this also has more overhead (sizeof(std::optional<std::reference_wrapper<T>>) is twice the size of T* ),
and let's not even start talking about calling conventions and the compile time cost of having to include both <functional> and <optional> everywhere.It's easy to tell that in the second function.
Second function makes for a better code review.
The first has no such indication, except hopefully some human-readable comment that the pointer may be NULL.
I don't understand. If the parameter was not meant to be sometimes null it would be a reference; not a pointer. The fact that a pointer is used is the "this may be null" indication.
Many people have pointed out that C++17 has std::optional. While that's true, I don't think it'll deprecate raw pointers. I'll try to explain why.
Here's an example you can paste, compile and run:
#include <iostream>
#include <memory>
struct foo
{
int thingamajig;
};
void borrow_optional_foo(foo * borrowed)
{
if (borrowed != nullptr)
{
borrowed->thingamajig = 42;
}
}
int main()
{
std::unique_ptr<foo> owned = std::make_unique<foo>();
owned->thingamajig = 17;
std::cout << owned->thingamajig << std::endl;
borrow_optional_foo(owned.get());
std::cout << owned->thingamajig << std::endl;
return 0;
}
If you run it, it should print: 17
42
What would it look like if we wanted to use std::optional and get the same behavior? #include <iostream>
#include <functional>
#include <memory>
#include <optional>
struct foo
{
int thingamajig;
};
void borrow_optional_foo(std::optional<std::reference_wrapper<foo>> borrowed)
{
if (borrowed)
{
borrowed->get().thingamajig = 42;
}
}
int main()
{
std::unique_ptr<foo> owned = std::make_unique<foo>();
owned->thingamajig = 17;
std::cout << owned->thingamajig << std::endl;
borrow_optional_foo(std::make_optional(std::ref(*(owned.get()))));
std::cout << owned->thingamajig << std::endl;
return 0;
}
As you can see, there's a tradeoff involved. On the one hand, you get crystal clear, descriptive type: std::optional<std::reference_wrapper<foo>> is clearly an optional reference to foo.On the other hand, using it is absolutely atrocious: you have to write borrowed->get().thingamajig as opposed to borrowed->thingamajig, and std::make_optional(std::ref(*(owned.get()))) as opposed to owned.get()
Does it work? Absolutely. Is it crystal clear? Indisputably so. Will it deprecate raw pointers? I really, really doubt it, but that's just my opinion.
https://herbsutter.com/2013/06/05/gotw-91-solution-smart-poi...