> You need dynamic_cast for dynamic interface queries ("does this object support X?"), even in the absence of virtual inheritance.
I prefer using C-style casts for brevity, and implementing support queries like that explicitly (i.e. via a "type" member or GetType() function or whatever in the base class). Quite often, you wind up needing that information anyway (resulting in redundancy), and the C++ casts are extremely verbose and ugly.
> Why? It's a language feature that's there to help you enforce said discipline. What's the downside?
I've tried working with it, but in my experience this language feature very quickly consumes more cognitive energy (by having to maintain the list of friend classes and just having to deal with it being there, consuming space in the source) than it saves over maintaining discipline manually. (This maybe a consequence of me not having worked on a lot of C++ projects with a high (number of contributors)/(lines of code) ratio.)
> That the object is allocated at the same address as its base subobject is guaranteed by the standard, if I remember correctly.
Huh. All sources I could find seem to claim that it actually isn't, but maybe I'm looking at the wrong ones.
> Your particular assert is not, though, because long is not guaranteed to fit a pointer (and doesn't, on Win64).
My bad. I've mostly only used unixoid systems for too long now.
> printf over iostream
But printf is so much more legible when you have to format complex strings.
> passing struct or std::string to printf
Who does that?
> There's no benefit to using malloc/free whatsoever, and it forces you to cast.
I do value keeping portions of code that don't use C++ features in C for reusability purposes, and moreover there is the whole issue that it's easy to get confused about initialisation with int* a=new int (uninitialised)/new int() (=0). If you use malloc, it's clear to everyone that it's uninitialised.
> Tuple has syntactic support as of C++17:
Yes, ugly, non-principled syntactic support.
std::tuple<int,int> a = {1,2}; // { }, like most other instances of lists of things
auto [x,y] = a; // [ ], for some reason (not used for lists/tuples anywhere else)
auto [z,w] = {1,2}; // this doesn't work at all
Surely being able to abbreviate B=C; A=B; into A=C; is a sane thing to expect when there is no good reason against it.
> If you ditch exceptions, you might as well ditch constructors as well, since there's no way to report errors from them otherwise. You're also forced to use new(nothrow) then. Basically, the language is designed around exceptions, and excluding them is fighting it.
I've worked with a number of embedded platforms that had C++ support without exceptions. As far as I know, there are also still issues when exceptions are thrown through non-C++ stack frames (e.g. a callback invoked from a C library). I'm aware of the problem of errors in constructors, but think that in general it is best to try and write code so that constructors don't do anything that can cause errors (I strongly prefer constructors to only serve the role of putting the object in a coherent state, and having a separate Init function for any nontrivial operations that could fail.)
Memory allocation may be an exception to this, but I feel like code where the sane reaction to OOM is not to treat it as a panic-abort condition is special enough that special measures such as an provisioning an explicit "constructed, but no memory has been acquired" state in your objects are justified.
I really do want exceptions to work - having worked with Standard ML for a long time, I'm well aware of how right they feel as a solution to error handling - but the C++ implementation of them still feels like it ultimately has way too many moving parts to be trustworthy.
(Edit: The new * italics syntax on HN is really throwing me off.)