I couldn’t fathom starting a new project with whatever the current C++ is now.
I couldn’t fathom starting a new project with whatever the current C++ is now.
As someone who grew up with modern C++, I can't even imagine going back to C++98 because it feels so incredibly verbose. Just compare how you iterate over a std::map and print its items in C++98 vs C++23:
// C++98:
for (std::map<std::string, int>::const_iterator it = m.begin(); it != m.end(); ++it) {
std::cout << it->first << ": " << it->second << "\n";
}
// C++23:
for (const auto& [key, value] : m) {
std::print("{}: {}\n", key, value);
}
Then there are all the features I would miss, for example: - auto
- lambda functions and std::function
- move semantics
- std::unique_ptr and, to a lesser extent, std::shared_ptr
- variadic templates
- std::filesystem
- std::chrono
- std::thread, std::mutex, std::atomic, etc.
- a well-defined memory model for multi-threaded programs
- unordered containers
- structured bindings
- class template argument deducation
- std::format
- std::optional
- std::variant
- etc.But when you try to use all these funny features you're enumerating there for something serious, it will invariably end up in an overcomplicated slow compiling morass. Even just trying to make the types click for inserting something into a non-trivial templatized hashmap becomes a tedious act, and the IDE cannot help anymore either.
(Last issue I had was with catching some exception just to ignore it. Turned out catch(std::bad_alloc) doesn't work, you need write catch (std::bad_alloc&).)
I prefer writing simple C-style C++ where I write whole subsystems from scratch, and I can be very clear about the semantics from the start, design in what matters, and leave out what doesn't. Adding all the built-in object semantics baggage is too much overhead.
Modern C++ is easier and safer than it has ever been.
The biggest slight is simply that there are high quality alternatives.
You can still write things the old way, if you like.
everything has been going downhill since then. coincidence? i think not!
C++14:
- generalized lambda capture
- generic lambdas
C++17: - structured bindings
- init statement for if
- class template argument deduction (CTAD)
- std::string_view
- std::filesystem
- std::variant
- std::optional
- std::to_chars() and std::from_chars()
C++20: - std::format
- coroutines (makes ASIO code so much cleaner!)
- concepts
- std::span
- bit manipulation (<bit>)
- std::bind_front
- std::numbers (math constants)My only complaint is the verbosity, things like `std::chrono::nanonseconds` break even simple statements into multiple lines, and you're tempted to just use uint64_t instead. And `std::thread` is fine but if you want to name your thread you still need to get the underlying handle and call `pthread_setname_np`. It's hard work pulling off everything C++ tries to pull off.
Yes, but here we're getting deep into platform specifics. An even bigger pain point are thread priorities. Windows, macOS and Linux differ so fundamentally in this regard that it's really hard to create a meaningful abstraction. Certain things are better left to platform APIs.
// To lessen verbosity, try defining the following convenience aliases in a header:
using SystemClock_t = std::chrono::system_clock;
using SteadyClock_t = std::chrono::steady_clock;
using HighClock_t = std::chrono::high_resolution_clock;
using SharedDelay_t = std::atomic<SystemClock_t::duration>;
using Minutes_t = std::chrono::minutes;
using Seconds_t = std::chrono::seconds;
using MilliSecs_t = std::chrono::milliseconds;
using MicroSecs_t = std::chrono::microseconds;
using NanoSecs_t = std::chrono::nanoseconds;
using DoubleSecs_t = std::chrono::duration<double>;
using FloatingMilliSecs_t = std::chrono::duration<double, std::milli>;
using FloatingMicroSecs_t = std::chrono::duration<double, std::micro>;
```