4 ms·
The main issue I have with C++ is that you basically have to relearn it from scratch every time a standard comes out. C++11 threw away decades of best practices
by qalmakka 6y ago
The main issue I have with C++ is that you basically have to relearn it from scratch every time a standard comes out. C++11 threw away decades of best practices, and C++17 again changed so many things it's hard to keep track of them (for instance, stuff like `string_view` has had a _huge_ impact on how I write code and how I reason about strings, making `const char *` almost disappear from some of my codebases).
C++20 with concepts and ranges will revolutionise everything, again, because those features are so big and pervasive they make lots of patterns and "older" parts f the language suddenly feel like "legacy". For instance, I see concepts vastly reducing the need for inheritance in lots of cases, massively simplifying what lots of folks like me were already doing with SFINAE and templates.
- tsimionescu 6y agoAs far as I understand, that is the whole point of many of these changes: there are patterns that people have to bend their code into and unsafe constructs that need to be used with care, and the newer standards seek to replace patterns with language features (e.g. concepts more or less instead of SFINAE), or bring safe alternatives to the previously necessary unsafe ones (string_view instead of `char*`). Now, the major concerns that some have is that this will not be successful, and that instead of replacing X with Y, you'll be left with both X and Y in the language as still necessary constructs for new code, making the language ever more complicated even if you can disregard the legacy. C++ already suffers somewhat from the strong ties between many of its features (for example, many people would like to use C++ without exceptions, but then you can't use `new` or constructors that can fail, and without constructors you can't use RAII, and so on).
- spacechild1 6y agoJust FYI > but then you can't use `new` You can certainly use new without exceptions, see https://en.cppreference.com/w/cpp/memory/new/nothrow https://en.cppreference.com/w/cpp/memory/new/nothrow or https://stackoverflow.com/a/15292148 https://stackoverflow.com/a/15292148 > and without constructors you can't use RAII, and so on). I think RAII is more about destructors (which must never throw exceptions). You can certainly write RAII style without throwing constructors. Generally, exceptions depend on RAII, but not the other way round.
- tsimionescu 6y ago> You can certainly use new without exceptions, see https://en.cppreference.com/w/cpp/memory/new/nothrow https://en.cppreference.com/w/cpp/memory/new/nothrow or https://stackoverflow.com/a/15292148 https://stackoverflow.com/a/15292148 You can use new without exceptions, but you can't detect OOM then, if I'm not mistaken. > I think RAII is more about destructors (which must never throw exceptions). You can certainly write RAII style without throwing constructors. Generally, exceptions depend on RAII, but not the other way round. The whole idea of RAiI is that you Acquire a Resource in the constructor (during Initialization), so that the destructor can guarantee it gets de-allocated. If you construct your object and then acquire a resource in it later, from a function that can return an error without throwing, then you introduce complexity in the destructor and may risk errors more.
- qalmakka 6y ago> You can use new without exceptions, but you can't detect OOM then, if I'm not mistaken. AFAIK `new (std::nothrow) T` returns nullptr if allocation fails, like std::malloc.
- spacechild1 6y agoExactly! It's worth pointing out that 'new (std::nowthrow) T' itself also performs a NULL check, so it doesn't call the constructor if the allocation failed.
- spacechild1 6y ago> The whole idea of RAiI is that you Acquire a Resource in the constructor (during Initialization), so that the destructor can guarantee it gets de-allocated Yes, that's the meaning of the acronym means, but unfortunately it's really a misnomer. When we say RAII style programming, we rather mean scoped destruction to avoid manual resource management. A classic example would be using std::lock_guard over a raw std::mutex. Some RAII objects don't throw on resource acquisition failure (std::fstream) or they expect that resource acquisition can fail (std::unique_lock with std::try_to_lock tag). Other RAII objects might not require any resource in the constructor at al, instead their only job is to release the resource that is given to them, like std::unique_ptr.
- qalmakka 6y agoYes, that's definitely a possibility, but I think most of the time when two patterns keep existing, instead of a new one just replacing the old one, is most of the time due to people refusing to change the ways they write code. For instance, `new` is basically nothing short of obsolete in modern C++, unless when used in "placement new". There are legally 0 reasons for ever writing `new T`, unless you are implementing `std::make_unique()` in the STL. Disabling exceptions is also IMHO a very poor choice - first, because there is no valid performance reason to do so, and second because it basically cripples half of the STL (and lots of other libraries). Using it becomes then tricky to use safely, because basically everything might just call std:: terminate () anywhere without any formal specification, or without being declared `noexcept`, by just changing compiler options. C++ exceptions, when used sparingly and for, well, exceptional events, are not as bad as people think they are. What's very unfortunate is that they got a very bad rap in the 90s, and then countless developers have been mislead by Java's unfortunate design choices, so they often fail to understand that handling failures should always considered part of the ordinary code path, and not treated as exceptional at all. I personally use exceptions as if they are a lighter version of assert, like `panic` from Rust or Go, in order to indicate that some basic code assumption has been violated, and thus executing a certain action is no longer possible (like, some parameter is malformed and you specifically asserted it's not, and such). Using them to represent normal runtime failures, such as IO errors, is sloppy design, and it's something that is much better accomplished by returning error values (which might also consist in enum classes or an error struct).