3 ms·
Personal opinions on the "right subset" of C++: * Yes to classes. * Yes to inheritance and virtual member functions, because interfaces are a very useful and
by 4bpp 8y ago
Personal opinions on the "right subset" of C++:
* Yes to classes.
* Yes to inheritance and virtual member functions, because interfaces are a very useful and easy to understand abstraction. Moreover, especially for games, idioms such as "class Monster : public DamageableObject" are really the most natural thing. However, avoid situations in which you need dynamic_cast like the plague. No to virtual inheritance.
- I really wish class memory layout were standardised more, but at least some non-standard assumptions are fairly safe in my experience: e.g. class A : B {...} ... B * x; assert((long)static_cast<A* >(x) == (long)x);
* Before you start using "friend class", consider whether you could get around it somehow. If a class is not public-facing, set everything public and enforce encapsulation by self-discipline rather than language features.
* Yes to templates in their C++ 101 application of making a $type-specialised version of classes and functions. No to using them to perform any real compile-time computation, or anything of the type Boost pulls. No to Boost.
* "Keep the parts of your code that only do C stuff in C".
- printf over iostream.
- (de)allocate classes with new/delete, but POD with malloc/free
- exception: std::string over char* , because it is really that much more convenient
* Yes to lambdas, but only pass them as function arguments if there is no good alternative, because of legibility. Avoid stuff like the for_each(..., []{ ... }) in the Ranges example.
* Yes to STL containers and algorithms, because everyone knows how they work and they are usually good enough.
* Range-based for over for(std::container::iterator i=...). Using the Ranges TS when a plain for loop would do strikes me as novelty-chasing and violates "do C stuff in C".
* Cautious yes to auto. It does somewhat detract from comprehension in some use cases. Don't just auto everything because you are too lazy to figure out the type.
* std::shared_ptr only when you actually think you can't answer the question of what the appropriate point to deallocate a piece of memory is, or at least not without significantly changing the structure of your code.
* Avoid std::tuple, std::variant and other awkward STL attempts to replicate functional language idiom without the syntactic support. It's terrible to read. Unfortunately, STL containers sometimes force you to use pair.
* No to exceptions; their interaction with other language features is awkward, and the eternal difficulties with platform support make me feel no confidence in the reliability of the feature. (I'm unfamiliar with the bowels of the implementation, but it seems like unwinding C++ stacks is fundamentally a hard problem.)
* No to novelty features such as user-defined literals.
* goto only to break out of multiple levels of nested loops.
* Coroutines strike me as something that's unambiguously cool but so far removed from the standard idiom of C++ programming that I'm inclined to say no. Maybe there's an alternative style guide you could write that says yes to coroutines and no to some of the things above. Multi-paradigm should not mean "use all the paradigms".
(I'm very happy to be persuaded otherwise on any of these points.)
- thestoicattack 8y agoNo std::unique_ptr?
- 4bpp 8y agoI haven't encountered any context in which it would add value (i.e. you can't just figure out yourself where it will go out of scope and delete at that point, which also makes the code clearer), and all other things equal, using it hides the semantically relevant type (the interesting part of a unique_ptr<A> is the A, not the unique_ptr!) and makes code and error messages harder to read.
- asdkfjasl 8y agounique_ptr is super useful for documenting the point in the code that owns a given object, and for providing some compile time protection against multiple deletions. (If you never write delete and only use unique_ptr, you have to do a relatively foolish thing to get double deletions.)
- 4bpp 8y agoI've written some heap-heavy code and never used unique_ptr, but I don't remember ever causing a double deletion. What's a pattern which you figure makes one prone to doing that? (On that basis, I'm a little inclined to suspect that it may be more of an issue for programmers who came from a deeply memory-managed language like Java and therefore don't build mental models to keep track until when a given allocation is needed.) (I have of course produced my share of memory leaks - praise be to valgrind - but they were all in scenarios where unique_ptr would have been too restrictive (without a Rust-style complete reconsideration of the code's ownership structure).)
- summerlight 8y agoIf code is completely under your control, then it's okay. But from my experience, these kinds of errors frequently stem from large code bases without a clear ownership. Think about a case that hundreds of people are working on the same code base. You may not understand 99% of code, and probably don't even want to understand all the horrors written by other programmers. In fact, I have seen a number of horrific instances that someone decided to write clever tricks with object lifetime which leads to production crash and no one really understand (or even bother) what's going on there. The only way to deal with such code is looking into its implementation, probably for a week or two. And found that the original author has left the company. In these cases, static type annotation helps a lot by serving as a formal contract. To reason about object lifetime, you don't need to dig into the implementation; all you need is looking into variable's type signature. Same thing can be applied to types other than those related to object lifetime.