Back to the Basics! Essentials of Modern C++ Style

来源:互联网 发布:济南公交线路查询软件 编辑:程序博客网 时间:2024/04/28 04:34

Herb Sutter @ CppCon2014, video can be found here.

Default == don’t overthink. Esp. don’t optimize prematurely.

  • Write for clarity and correctness first.
  • Avoid premature optimization. By default, prefer clear over optimal.
  • Avoid premature pessimization. Prefer faster when equally clear.

Raw Pointers vs Smart Pointers

  • Express ownership using unique_ptr wherever possible, including when you don’t know whether the object will actually ever be shared. That is, write make_unqiue by default or make_shared when needed.
  • Don’t use Owning *, new or delete any more, except rarely inside the implementation details of low-level data structures.
  • Do use Non-Owning * and &, especially for parameters. Prefer passing objects by * or by & as usual, just like always.
  • When deferencing a global (static or heap) or aliased local refcounted pointers e.g. shared_ptr<widget> g_p, take its unaliased + local copy at the top of a call tree auto pin = g_p otherwise g_p might disappear.
    deref.unaliased.local.refcounted.ptr
  • Don’t copy/assign refcounted smarted pointers, including pass-by-value or in loops, unless you really want the semantics they express: altering object lifetime. In that case, refer to the following code.
unique_ptr<widget> factory(); // source - produces widgetvoid sink(unique_ptr<widget>); // sink - consumes widgetvoid reseat(unique_ptr<widget>&); // "will" or "might" reseat ptrvoid thinke(const unique_ptr<widget>&); // usually not what you wantshared_ptr<widget> factory();// source + shared ownershipvoid share(shared_ptr<widget>); // share - "will" retain refcountvoid reseat(shared_ptr<widget>&); // "will" or "might" reseat ptrvoid may_share(const shared_ptr<widget>&); // "might" retain refcount

Use auto whenever possible

Prefer declaring local variables using auto, whether the type should be track or stick. This guarantees zero implicit coversions/temporaries, zero narrowing conversions and zero uninitialized variables.

  • Deduced and exact, when you want tracking: auto x = init;
    Q: Does this “=” create a temporary objects plus a move/copy?
    A: Standard says “No”. The code T x = a; has exactly the same meaning as T x(a); when a has type T (or derived from T)… and auto x = a; guarantees the types are the same, so it always means exactly the same as auto x(a).
  • Write explicit typename, when you want to commit: auto x = Type{init};
    Q: Does this “=” create a temporary objects plus a move/copy?
    A: Standard says “Yes, but”: the compiler elide the temporary. In practice compilers do (and in the future routinely will) elide this
    temporary+move. However, the type must still be movable (which
    includes copyable as a fallback).

The C++ world is moving to left-to-right: Auto variables, standard-defined literals, User-defined literals, Function declarations (tailing return type or just auto since C++14, Named lambdas, Aliases with using (no more typedef), Template aliases. As to auto variables, here is another example where auto is better:

base* pb = new derived();unique_ptr<base> pb = make_unique<derived>(); // too subtle: people keep not seeing itauto pb = unique_ptr<base>{make_unique<derived>()}; // explicit and clear: hard to miss it

There are cases where you can’t use “auto style”: type{} with non-(cheaply-)movable type.

auto lock = lock_guard<mutex>{m}; // error, not movableauto ai = atomic<int>{}; // error, not movableauto a = array<int, 50>{}; // compiles, but needlessly expensive

Return-by-value vs Pass-by-value

Use return-by-value way more often, but don’t overuse pass-by-value.

Just as exception safety isn’t all about writing try and catch, using move semantics isn’t all about writing move and &&.

advice

advanced.advice

narrowly.useful.advice

Howard Hinnant: “Don’t blindly assume that the cost of construction is the same as assignment.”

For strings and vectors, capacity plays a large role in their performance. Copy construction always allocates (except for short). Copy assignment (except for short) allocates/deallocates 50% of the time with random capacities on the lhs and rhs. To keep an eye on performance, one must count allocations and deallocations.

Constructors are the primary case of multiple “in + retain copy” (pass-by-value) parameters, where overloading const&/&& is combinatorial.


Uses and Abuses of Forwarding Reference

The name of && is expected to be changed from universal reference to forwarding reference in the new print of Scott Meyer’s “Effective Modern C++”.

  • Use && only for parameter/return types. (1)myclass&& rvalue references to optimize rvalues, usually overloading const& /&&.
    (2)T&& forwarding references to write forwarders, which are neutral code between unknown callers and callees and want to preserve rvalueness/cv-ness.
  • Don’t use auto&& for local variables. You should know whether your variable is const/volatile or not except rarely if you’re just handing it off in the body of a forwarder

0 0
原创粉丝点击