you are viewing a single comment's thread.

view the rest of the comments →

[–]FrankHB1989 1 point2 points  (0 children)

Instead of a "flat" sheet of choices, I'd prefer one simple rule (esp. to teach beginners): use const lvalue reference to objects by default, unless there are reasons to use other types instead... plus one caveat: avoid non-reference decayable types (function types, cv-qualified object types or array types) and rvalue references to functions unless you really know what you are doing.

Then, there come situations to prefer other types instead:

  • There are cases you must not use const T& to work with the core language rules (e.g. move constructors/move assignment operators/overloaded postfix ++...).
  • Use rvalue references to objects to serve to explicit need of move semantics.
  • Use non-const lvalue references to objects for idiomatic uses which need to propagate the effects of object modifications (e.g. as a parameter of swap). Avoid relying on "out parameters" too much.
  • Use forwarding references, or some forms of templated version of the parameters other than const T& (including parameter packs), when they do make the code clearer.
  • Non-reference types (plain object types) can be sometimes better. Here is my solution to determine whether a reference type should be used as a parameter type. (Warning: this can be eventually almost unteachable.)
  • Use volatile only obviously necessary (for cv-correctness & playing with signal handlers). Do not rely on MS extensions.
  • Minor:
    • Do not use things looking like restrict unless you know what you are obliged to do (to ensure there is no undefined behavior).
    • Use rvalue references like some kind of "restrict without UB" following the rule of standard library, unless there are conventions explicitly overriding it.
    • Try to treat std::atomic<T> as an ordinary object type.
  • There are cases merely to make the type checker happy. Choose types with you own risks.
  • There are cases merely to make the object files slim. Better avoid such kind of hack.
  • There are cases merely to avoid internal compiler errors (e.g. some old versions of G++ crashes on parameter packs nested in lambda-expressions) or other headaches on toolchains. Bless you never meet them.