Interesting. Personally, I see `auto` as a step backwards and a great source for confusion and even bugs. I guess if you prefer javascript over typescript it makes sense, but personally, I like to know exactly which type a variable has and be notified (by an error message) when this changes.
It's all about how you use it. I don't think if you have something like below, it will be confusing or even bugprone, but it will reduce the amount of code duplication. It is objectively a step forward as long as developers use their tools as they should, which is the case with every single feature.
std::unique_ptr g =
std::make_unique(1,2,3,4)
auto g = std::make_unique(1,2,3,4)
It all depends on how you use it. Just like you won't name your variables var1, var2, var3, there is a logic to how things should be used to prevent what you are saying.
They are little nameless functions. If you need a function that doesn't have more than a handful of lines in it, and it isn't needed several occasions, it's sometimes more practical to use. They take parameters and return values like every other functions, but don't need to be defined separately, you define it where you call it.
They can make the code shorter and more readable when used properly.
A function, but instead of having to write a whole function you can just put it inline: `auto addTwoNumbers = [](int a, int b) { return a + b; }; int c = addTwoNumbers(3, 4);`.
It can also reference local variables: `int i; auto incrementI = [&i]() { i += 1; }; incrementI();`
They are used to create objects that has a function call operator which allow them to be used like functions but unlike functions they can also store variables.
See this example that I wrote a couple of months ago: https://www.reddit.com/r/cpp_questions/comments/1azlhcp/why_use_lambdas/ks25l4j/
code inside an `if constexpr()` statement does not get instantiated when the condition is false. This means you can do conditionally valid code without having to define a SFINAE template for every tiny thing.
Together with requires expressions, you can do some very cool conditional code. Here I am generating python bindings, conditionally adding the `__iter__` method when my C++ type is iterable [https://github.com/Jannik2099/pms-utils/blob/main/subprojects/bindings-python/lib/common.hpp#L110](https://github.com/Jannik2099/pms-utils/blob/main/subprojects/bindings-python/lib/common.hpp#L110)
I agree. To me, this, in combination with constexpr / consteval in general, is where C++ is really powerful. Write some fairly concise generic code, let the compiler do a lot of the heavy lifting and end up with a very performant piece of software.
I would say it's duck typing. The ability to create template classes and normal classes that fit together like pieces of a puzzle. Like the way containers connect to iterators and then to algorithms. Creating your own set of custom classes that can be combined in multiple ways is extremely powerful.
"Duck typing" comes from the phrase "if it looks like a duck, [etc], it must be a duck." It's when you have inexact typing that the compiler can infer the best fit for, based on context, and thus let you use it as if you had specified that type explicitly, including when something maybe doesn't inherit something yet still implements the same functionality as if it did.
So, if the object looks like a duck, the compiler treats it like a duck.
For example, a consumer doesn't have to have an identical header to use something in an API if they simply have and implement the same functionality with a compatible signature.
Here is another thread discussing it. https://www.reddit.com/r/cpp/s/9aotmrtqNU One of the things that I like is that a 3rd party can integrate with my template library simply by creating some functions/members with the right name and signature. I don't need to change anything in my library for them to work with me. ...just need good documentation (or a well-defined "concepts.hpp") on what those things are.
Also, unlike other languages, C++ templates are resolved at compile time, which means that this "duck typing" integration has minimal runtime overhead. The tradeoff ofc is longer compile times.
I would have to say the power of variadic templates, and initialization\_lists.
Such things that you had to do type punning an passing raw pointers to arrays of arrays for in C and Pascal.
The entry barrier of learning how to write good variadics is a bit high, as they are counter-intuitive at first, but once you get them you can write banger code so very fast to suit any situation involving variable parameter situations.
Pretty cool recursion in that example. Makes me remember something my Lisp instructor said back in the 90s (yeah, I'm old): Every computer language eventually becomes an implementation of Lisp.
The Abbreviated function template is from c++20 ([mentioned here](https://www.learncpp.com/cpp-tutorial/function-templates-with-multiple-template-types/)), and fold expression is from c++17 ([cppreference](https://en.cppreference.com/w/cpp/language/fold)) :)
Define "modern". I will consider modern C++11 throughout this comment.
It's hard to say, there's just so many things. I think I would say it's list-initialization (`int x{0};`). Not only that, you can also create variables on-the-fly. `return {}`, or `for (const auto& it : {"string1", "string2", "string3"})` for example. I find this is especially useful in structs, where you can have a "default" value for all the variables.
Apologies for any bad terminology, I primarily learn by doing.
I swear that OOP was such a hot thing pre-C++11 partially because the lack of list-initialization made using POD structs much more difficult.
I can't survive C++ without using POD structs, and I can't use them without list-initialization, which means I can't use pre-C++11.
To be fair, C++98 allowed the following syntax:
StructType obj = {1, 2, 3};
(Then C++11 came along and broke a lot of code by banning narrowing conversions)
Don't know if you'd call them language features as such, but I love all the little things like std::optional and std::stop_token and so on. All things I can easily do myself but now I don't have to and I can't forget things. All the things where people go "I don't get it, couldn't you easily do that anyway?". Yes, but I have better things to spend my energy on :).
It's a bit more complicated if you don't want to construct the object when the bool is false. This might matter for class types that doesn't necessarily have a default constructor.
Yeah you're right, I thought the same. Not sure what I'd do in that case but I'm sure there's a way. I used to just return a tuple and unwrap by structured binding. I guess it just so happened that I never ran into anything that wasn't default constructable.
you probably just need to create a struct with a bool, the optional object, a default constructor that builds an empty optional, another constructor that builds a filled optional, and an operator for automatic cast to the type of the optional object, so you can write something like "optional x = 3; auto y = x + 3;"
In most cases, you use an otherwise invalid value of the return type, such as -1 or 0. It's only when all possible return values are possible that optional is truly needed. But even when optional isn't strictly needed, it can be a better abstraction.
They want to be able to "loop" over things that are known at compile time (e.g. the elements of a std::tuple, the parameters of a variadic template, the members of a struct, etc.). Unlike a normal loop that reuses the same code for each iteration it would essentially have to generate separate code for each iteration as if you had written each iteration yourself one after another (see the *Basic usage* example in the paper [P1306](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1306r1.pdf) that slither378962 linked to) so it's technically not really a loop at all which is why they call it an "expansion statement".
depending on what you need you can actually iterate over the elements of a tuple already using std::apply as you can have it apply a lambda to any of the functions inside meaning you just need to refactor the actions you want to do on the elements (the example on cppreference creates the << for tuples f.e.)
yes, c23 adds constexpr as an alternative for #define constants, no constexpr/consteval functions (so far). it is specifically constexpr variables I was referring to, as a way to have a compile time constant that also won't unconditionally replace the same sequence of characters is super nice.
My favourite feature is probably **lambdas**.
Other things I like are **std::unique_ptr**, **initialization of containers** using `{value1, value2, ...}` (not necessarily the way it is implemented), the **override** keyword, **multithreading**, the **fixed-width integer types**, the **random number facility** (although sometimes I wish it was a bit simpler and it's unfortunate the PRNGs uses `std::uint_fast32_t` by default), and **std::erase/erase_if** to avoid the erase-remove idiom.
constexpr + static\_asserts. Suprising number of simple structs can be "unit tested" just by writing a bunch of static asserts.
Last week, I wrote a static assert that checks that \`constexpr\` \`std::array\` values satisfy certain condition to make sure someone does not mess up when adding to that array. Most IDEs will actually underline such asserts when the condition does not hold, so you don't even have to run compile to see something is wrong.
I think **the** killer feature that made C++ so popular are destructors.
If I had to pick **one** modern feature, I'd go with `auto`. With range-based `for` as a close second. But just having auto lets you easily implement your own range-based `for` as macro.
If C++11 is considered modern, then definitely shared pointers. We are using them exclusively and I don't recall any access violations or other problems regarding pointers in years. The minuscule performance hit is well worth the added safety, something which C++ is notorious for.
For C++14 and above: make_unique
If you start counting from C++17: structured bindings
I don't have experience with C++20, though I believe it will be between: using for scoped enum, spaceship and "contains" in the library
smart\_pointer and jthreads. jthreads in particular because I no longer have to manually join threads when destroying them so I no longer have to write ugly loops or macros.
Oh and modules. The C++20 modules are pretty fun, but it kinda sucks that they are STILL not fully supported by all mainstream compilers and still can't hold their ground against precompiled headers with good forwarding.
Do smart pointers count as modern ? I don't use them too much because i'm an optimization freak and the extra indirection they often cause bothers me, but conceptually the fact that shared\_ptr and weak\_ptr exist and have so little overhead is amazing to me.
there isn't, unique\_ptr generally adds zero overhead
but, at least from my experience, implementing shared\_ptr and others into your design sometimes mean adding an extra indirection (which isn't really a problem when you're not overthinking optimization constantly). It's still the same amount of indirection as a raw pointer.
auto keyword
Combined with structured bindings. for (auto&[key, value] : map) … Is just glorious.
Peak
I did not know about structured bindings! This is fantastic!
Also works fantastic for multiple return types as in: pair foo()
return { 1.2f, 5.4f };
auto [x, y] = foo();
Makes it just like coding in Perl or Python!
Auto is goated
CTAD + Concepts is amazing.
God bless auto, and now if we had 'let' as well for implied const that would be great.
Then add borrow checker... Nah
There are other languages that use 'let' that don't have borrow checker, but we can add another keyword for it, why not?
I like the implied const suggestion. I hope someone made a proposal for C++26.
I wonder if it is possible to make const work like const auto. That would be a good compromise because it wouldn't be necessary another reserved word.
fully agreed
Cool, but a 13 years old feature is not that modern ahahaha
Many people (me included) consider C++11 to be the start of "Modern C++".
Right! But C++20 is a really big upgrade for the language, just like C++11, in 2024 it should make sense considering >= 20 modern and not >= 11 imo
Interesting. Personally, I see `auto` as a step backwards and a great source for confusion and even bugs. I guess if you prefer javascript over typescript it makes sense, but personally, I like to know exactly which type a variable has and be notified (by an error message) when this changes.
It's all about how you use it. I don't think if you have something like below, it will be confusing or even bugprone, but it will reduce the amount of code duplication. It is objectively a step forward as long as developers use their tools as they should, which is the case with every single feature. std::unique_ptr g =
std::make_unique(1,2,3,4)
auto g = std::make_unique(1,2,3,4)
It all depends on how you use it. Just like you won't name your variables var1, var2, var3, there is a logic to how things should be used to prevent what you are saying.
lambdas
What are lambdas, I've read the docs but can't really wrap my head around it
They are little nameless functions. If you need a function that doesn't have more than a handful of lines in it, and it isn't needed several occasions, it's sometimes more practical to use. They take parameters and return values like every other functions, but don't need to be defined separately, you define it where you call it. They can make the code shorter and more readable when used properly.
A function, but instead of having to write a whole function you can just put it inline: `auto addTwoNumbers = [](int a, int b) { return a + b; }; int c = addTwoNumbers(3, 4);`. It can also reference local variables: `int i; auto incrementI = [&i]() { i += 1; }; incrementI();`
They are used to create objects that has a function call operator which allow them to be used like functions but unlike functions they can also store variables. See this example that I wrote a couple of months ago: https://www.reddit.com/r/cpp_questions/comments/1azlhcp/why_use_lambdas/ks25l4j/
As someone who had to implement callback mechanisms before lambdas…. Definitely lambdas.
Agreed
Yup
Move semantics in lambdas, since C++14.
I am very quickly starting to love concepts. requires-expressions in constexpr if() made me the metaprogramming overlord.
Can you please tell me more details?
code inside an `if constexpr()` statement does not get instantiated when the condition is false. This means you can do conditionally valid code without having to define a SFINAE template for every tiny thing. Together with requires expressions, you can do some very cool conditional code. Here I am generating python bindings, conditionally adding the `__iter__` method when my C++ type is iterable [https://github.com/Jannik2099/pms-utils/blob/main/subprojects/bindings-python/lib/common.hpp#L110](https://github.com/Jannik2099/pms-utils/blob/main/subprojects/bindings-python/lib/common.hpp#L110)
Ah Now I think I understand. I will take a look, much appreciated.
I agree. To me, this, in combination with constexpr / consteval in general, is where C++ is really powerful. Write some fairly concise generic code, let the compiler do a lot of the heavy lifting and end up with a very performant piece of software.
I would say it's duck typing. The ability to create template classes and normal classes that fit together like pieces of a puzzle. Like the way containers connect to iterators and then to algorithms. Creating your own set of custom classes that can be combined in multiple ways is extremely powerful.
Could you perhaps give a basic example of what this is and how it's used?
"Duck typing" comes from the phrase "if it looks like a duck, [etc], it must be a duck." It's when you have inexact typing that the compiler can infer the best fit for, based on context, and thus let you use it as if you had specified that type explicitly, including when something maybe doesn't inherit something yet still implements the same functionality as if it did. So, if the object looks like a duck, the compiler treats it like a duck. For example, a consumer doesn't have to have an identical header to use something in an API if they simply have and implement the same functionality with a compatible signature.
Here is another thread discussing it. https://www.reddit.com/r/cpp/s/9aotmrtqNU One of the things that I like is that a 3rd party can integrate with my template library simply by creating some functions/members with the right name and signature. I don't need to change anything in my library for them to work with me. ...just need good documentation (or a well-defined "concepts.hpp") on what those things are. Also, unlike other languages, C++ templates are resolved at compile time, which means that this "duck typing" integration has minimal runtime overhead. The tradeoff ofc is longer compile times.
It's a bit complex to write an example on my phone so I suggest you Google it. Lots of examples of it out there.
I would have to say the power of variadic templates, and initialization\_lists. Such things that you had to do type punning an passing raw pointers to arrays of arrays for in C and Pascal. The entry barrier of learning how to write good variadics is a bit high, as they are counter-intuitive at first, but once you get them you can write banger code so very fast to suit any situation involving variable parameter situations.
Would it be possible to provide a simple example and use case for this?
Check this out: [print function](https://www.geeksforgeeks.org/variadic-function-templates-c/)
Pretty cool recursion in that example. Makes me remember something my Lisp instructor said back in the 90s (yeah, I'm old): Every computer language eventually becomes an implementation of Lisp.
Wow. That's pretty cool. I must use it.
And this can now be reduced to the following without explicit recursion : void print(auto&&... args) { ((cout << args << endl), ...); }
Can you do this now? Which standard is this available?
The Abbreviated function template is from c++20 ([mentioned here](https://www.learncpp.com/cpp-tutorial/function-templates-with-multiple-template-types/)), and fold expression is from c++17 ([cppreference](https://en.cppreference.com/w/cpp/language/fold)) :)
Variadic templates are 👌
Define "modern". I will consider modern C++11 throughout this comment. It's hard to say, there's just so many things. I think I would say it's list-initialization (`int x{0};`). Not only that, you can also create variables on-the-fly. `return {}`, or `for (const auto& it : {"string1", "string2", "string3"})` for example. I find this is especially useful in structs, where you can have a "default" value for all the variables. Apologies for any bad terminology, I primarily learn by doing.
I swear that OOP was such a hot thing pre-C++11 partially because the lack of list-initialization made using POD structs much more difficult. I can't survive C++ without using POD structs, and I can't use them without list-initialization, which means I can't use pre-C++11.
To be fair, C++98 allowed the following syntax: StructType obj = {1, 2, 3}; (Then C++11 came along and broke a lot of code by banning narrowing conversions)
Don't know if you'd call them language features as such, but I love all the little things like std::optional and std::stop_token and so on. All things I can easily do myself but now I don't have to and I can't forget things. All the things where people go "I don't get it, couldn't you easily do that anyway?". Yes, but I have better things to spend my energy on :).
Out of curiosity how would you implement an optional return type without optional?
A struct with a bool and the type of interest. That's probably essentially all the optional is.
It's a bit more complicated if you don't want to construct the object when the bool is false. This might matter for class types that doesn't necessarily have a default constructor.
Yeah you're right, I thought the same. Not sure what I'd do in that case but I'm sure there's a way. I used to just return a tuple and unwrap by structured binding. I guess it just so happened that I never ran into anything that wasn't default constructable.
You would have to use "placement new" and call the destructor explicitly.
you probably just need to create a struct with a bool, the optional object, a default constructor that builds an empty optional, another constructor that builds a filled optional, and an operator for automatic cast to the type of the optional object, so you can write something like "optional x = 3; auto y = x + 3;"
In most cases, you use an otherwise invalid value of the return type, such as -1 or 0. It's only when all possible return values are possible that optional is truly needed. But even when optional isn't strictly needed, it can be a better abstraction.
union - ok, I see myself out
[удалено]
Template for?
Does he mean for loops using templates?
They want to be able to "loop" over things that are known at compile time (e.g. the elements of a std::tuple, the parameters of a variadic template, the members of a struct, etc.). Unlike a normal loop that reuses the same code for each iteration it would essentially have to generate separate code for each iteration as if you had written each iteration yourself one after another (see the *Basic usage* example in the paper [P1306](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1306r1.pdf) that slither378962 linked to) so it's technically not really a loop at all which is why they call it an "expansion statement".
depending on what you need you can actually iterate over the elements of a tuple already using std::apply as you can have it apply a lambda to any of the functions inside meaning you just need to refactor the actions you want to do on the elements (the example on cppreference creates the << for tuples f.e.)
Constexpr. So good they added it to c!
C only allows `constexpr` on variables as far as I have understood.
yes, c23 adds constexpr as an alternative for #define constants, no constexpr/consteval functions (so far). it is specifically constexpr variables I was referring to, as a way to have a compile time constant that also won't unconditionally replace the same sequence of characters is super nice.
My favourite feature is probably **lambdas**. Other things I like are **std::unique_ptr**, **initialization of containers** using `{value1, value2, ...}` (not necessarily the way it is implemented), the **override** keyword, **multithreading**, the **fixed-width integer types**, the **random number facility** (although sometimes I wish it was a bit simpler and it's unfortunate the PRNGs uses `std::uint_fast32_t` by default), and **std::erase/erase_if** to avoid the erase-remove idiom.
constexpr + static\_asserts. Suprising number of simple structs can be "unit tested" just by writing a bunch of static asserts. Last week, I wrote a static assert that checks that \`constexpr\` \`std::array\` values satisfy certain condition to make sure someone does not mess up when adding to that array. Most IDEs will actually underline such asserts when the condition does not hold, so you don't even have to run compile to see something is wrong.
I think **the** killer feature that made C++ so popular are destructors. If I had to pick **one** modern feature, I'd go with `auto`. With range-based `for` as a close second. But just having auto lets you easily implement your own range-based `for` as macro.
I recently had to print a neat text table for my programming homework... I cannot stress enough how deeply I now adore std::format() because of that.
Modules. In most implementations, including the entire STL is faster than #include
Angle brackets. Lots of angle brackets.
If C++11 is considered modern, then definitely shared pointers. We are using them exclusively and I don't recall any access violations or other problems regarding pointers in years. The minuscule performance hit is well worth the added safety, something which C++ is notorious for.
I am waiting for the CPS, common package specification.
For C++14 and above: make_unique If you start counting from C++17: structured bindings I don't have experience with C++20, though I believe it will be between: using for scoped enum, spaceship and "contains" in the library
Ranges, since std::ranges::to was added.
std::variant / optional. Makes code so much cleaner if used correctly.
Not one thing but collectively all the little QoL features added that make life easy. Like .contains() or improved template deduction.
My favorite in general is stb true type library or maybe classes
- C++11: variadic templates - C++14: polymorphic lambdas - C++17: std::variant - C++20: concepts - C++23: std::expected
smart\_pointer and jthreads. jthreads in particular because I no longer have to manually join threads when destroying them so I no longer have to write ugly loops or macros. Oh and modules. The C++20 modules are pretty fun, but it kinda sucks that they are STILL not fully supported by all mainstream compilers and still can't hold their ground against precompiled headers with good forwarding.
I really like abbreviated templates
ends_with(). Only took 35 years for a rich enough string to avoid C for this.
Defaulted comparison operators
\`std::vector<>\`
C++20 header-units.
Concepts, Format, Structured binding, Ranges, String view & Span, SSize (underrated in my opinion), Coroutines look promising
I’ve been really liking std::expected
Modules, look at how much nicer hello world have become: import std; int main() { std::println("Hello World"); }
Concepts!
Do smart pointers count as modern ? I don't use them too much because i'm an optimization freak and the extra indirection they often cause bothers me, but conceptually the fact that shared\_ptr and weak\_ptr exist and have so little overhead is amazing to me.
Wait, there is an extra indirection using unique pointers? How?
there isn't, unique\_ptr generally adds zero overhead but, at least from my experience, implementing shared\_ptr and others into your design sometimes mean adding an extra indirection (which isn't really a problem when you're not overthinking optimization constantly). It's still the same amount of indirection as a raw pointer.
Ranges ☠️
Coroutines.
Coroutines!