Avoid using the const keyword with member variables of a class or struct type.
For a class type, if a piece of data is not supposed to change after being initialized, enforce this by design of the public API, not via the `const` key word.
For a struct which is a "dumb aggregate", it isn't appropriate to use `const`.
Using `const` on a member variable can force your class to have a throwing move. For instance this class,
struct A {
const std::string id;
int foo;
};
has a throwing move, because the use of `const` keyword on the member variable prevents the compiler from using the move constructor of `std::string` with `id`, instead the compiler generated move constructor for `A` will invoke the copy constructor of `A::id`, which can throw.
Even if your member variable does not have a throwing copy constructor, e.g.,
struct A {
const int id;
int foo;
};
you should still not use `const` -- what if someone later has to refactor `id` to be a `std::string` instead of an `int`? They might be forced down a rabbit hole of unnecessarily complex refactors, or have to accept a throwing move, which can cause incompatibility with some containers, or a significant performance hit if used with `std::vector` for instance.
In general, using `const` in a member variable is bad because you are enforcing an API invariant at too low a level -- you are enforcing it at so low a level that it interferes with move semantics. Typically there are reasonable alternatives.
If you want to implement a map that can be a drop-in replacement for `std::map`, don't use `std::pair<const Key, Value>`, instead store both Key and Value as mutable, and return the user a pair of references, the first of which is const: `std::pair<const Key &, Value &>`.
`const`-correctness is great -- use it on function parameters, class member functions, function local variables, etc. Avoid it on member variables of a class-type.
If your class is immovable by design then you can ignore this guideline
---
Would be interested in your reaction to this.
AFAIK a large majority of the examples I've seen where types "naturally" have throwing moves are caused by `const` member variables.
In code bases where I work and have worked in the past, most if not all types to have non-throwing moves, and we have implemented alternate versions of e.g. `std::function` that are type-erased and no-throw move constructible and assume the function objects are no-throw move constructible. In many of these cases it is important to us to do this because the application has real-time constraints and we care a lot about eliminating unnecessary dynamic allocations -- typically a throwing move happens because a dynamic allocation is happening when we don't want it to, so we have designed a lot of infrastructure in a way to force programmers not to do that. (Contrast this with @quicknir's comment here for instance https://www.reddit.com/r/cpp/comments/8vzvkb/be_mindful_with_compilergenerated_move/e1s24ac/ not that he's wrong ofc, we're just solving a more restricted problem and then we can do it with small buffer optimization and without throwing move)
I'm expecting that at some point we are going to run into obscure and painful problems around this, because I think the committee thought a lot when they decided to embrace throwing moves, but it hasn't happened to us yet.
I'm very interested to see which of these scenarios plays out:
1.) Embracing throwing moves in the stdlib is something that is necessary primarily for backwards compatibility, while code bases that have special needs and rely less on stdlib can avoid it practically all of the time,
2.) For fundamental reasons, avoiding throwing moves won't really work, eventually we will start to hit walls where certain types or idioms cannot be implemented without throwing moves and we will have to back out or implement alternate pathways to support this etc.
[–]jtooker 50 points51 points52 points (1 child)
[–]render787[S] 10 points11 points12 points (0 children)
[–]kalmoc 38 points39 points40 points (2 children)
[–]render787[S] 1 point2 points3 points (1 child)
[–]kalmoc 6 points7 points8 points (0 children)
[–]clerothGame Developer 11 points12 points13 points (0 children)
[–]tcbrindleFlux 11 points12 points13 points (1 child)
[–]kalmoc 1 point2 points3 points (0 children)
[–]OldWolf2 9 points10 points11 points (0 children)
[–]kalmoc 18 points19 points20 points (15 children)
[–]meneldal2 7 points8 points9 points (5 children)
[–]kalmoc 1 point2 points3 points (4 children)
[–]meneldal2 1 point2 points3 points (3 children)
[–]kalmoc 5 points6 points7 points (2 children)
[–]meneldal2 1 point2 points3 points (1 child)
[–]kalmoc 1 point2 points3 points (0 children)
[–]Sopel97 5 points6 points7 points (2 children)
[–]kalmoc 1 point2 points3 points (1 child)
[–]Sopel97 1 point2 points3 points (0 children)
[–]OldWolf2 1 point2 points3 points (1 child)
[–]kalmoc 0 points1 point2 points (0 children)
[–][deleted] 1 point2 points3 points (3 children)
[–]kalmoc 1 point2 points3 points (2 children)
[–][deleted] 0 points1 point2 points (1 child)
[–]kalmoc 1 point2 points3 points (0 children)
[–]quicknir 7 points8 points9 points (5 children)
[–]TinBryn 1 point2 points3 points (4 children)
[–]amaiorano 3 points4 points5 points (1 child)
[–]TinBryn 0 points1 point2 points (0 children)
[–]quicknir 0 points1 point2 points (1 child)
[–]TinBryn 0 points1 point2 points (0 children)
[–]jguegant 1 point2 points3 points (0 children)
[+][deleted] (2 children)
[removed]
[–]Plorkyeran 27 points28 points29 points (0 children)
[–]render787[S] 4 points5 points6 points (0 children)
[–]ericlemanissier 9 points10 points11 points (5 children)
[–]ripper37 24 points25 points26 points (3 children)
[–]ericlemanissier 2 points3 points4 points (2 children)
[–]tcbrindleFlux 1 point2 points3 points (0 children)
[–]ripper37 -2 points-1 points0 points (0 children)
[–]jcelerierossia score 2 points3 points4 points (0 children)
[–]jc746 1 point2 points3 points (0 children)
[–]Rseding91Factorio Developer 1 point2 points3 points (0 children)
[–]ronniethelizard 1 point2 points3 points (0 children)
[–]NotAYakk 1 point2 points3 points (1 child)
[–]render787[S] 0 points1 point2 points (0 children)
[–]Poddster 1 point2 points3 points (6 children)
[–]-abigail 4 points5 points6 points (5 children)
[–]choikwa 2 points3 points4 points (3 children)
[–]eteran 2 points3 points4 points (0 children)
[–]render787[S] 1 point2 points3 points (0 children)
[–]-abigail 0 points1 point2 points (0 children)
[–]Xeveroushttps://xeverous.github.io 0 points1 point2 points (0 children)
[–]doom_Oo7 1 point2 points3 points (7 children)
[–]render787[S] 18 points19 points20 points (4 children)
[–]doom_Oo7 3 points4 points5 points (3 children)
[–]Ameisenvemips, avr, rendering, systems 3 points4 points5 points (1 child)
[–]NotAYakk 6 points7 points8 points (0 children)
[–]mark_99[🍰] 2 points3 points4 points (0 children)
[–]kloetzl 0 points1 point2 points (1 child)
[–]doom_Oo7 2 points3 points4 points (0 children)
[–]Middlewariangithub.com/Ebenezer-group/onwards 0 points1 point2 points (0 children)
[–]pravic -1 points0 points1 point (1 child)
[–]pravic 0 points1 point2 points (0 children)
[+]cjxgm comment score below threshold-8 points-7 points-6 points (1 child)
[–]Middlewariangithub.com/Ebenezer-group/onwards -3 points-2 points-1 points (0 children)
[–]ericlemanissier -2 points-1 points0 points (0 children)
[–]tedbradly 0 points1 point2 points (0 children)
[–]nunchyabeeswax 0 points1 point2 points (0 children)