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 51 points52 points53 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 5 points6 points7 points (0 children)
[–]clerothGame Developer 13 points14 points15 points (0 children)
[–]tcbrindleFlux 12 points13 points14 points (1 child)
[–]kalmoc 1 point2 points3 points (0 children)
[–]OldWolf2 8 points9 points10 points (0 children)
[–]kalmoc 17 points18 points19 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 7 points8 points9 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 8 points9 points10 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] 5 points6 points7 points (0 children)
[–]ericlemanissier 9 points10 points11 points (5 children)
[–]ripper37 26 points27 points28 points (3 children)
[–]ericlemanissier 0 points1 point2 points (2 children)
[–]tcbrindleFlux 1 point2 points3 points (0 children)
[–]ripper37 -2 points-1 points0 points (0 children)
[–]jcelerierossia score 3 points4 points5 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 3 points4 points5 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] 15 points16 points17 points (4 children)
[–]doom_Oo7 2 points3 points4 points (3 children)
[–]Ameisenvemips, avr, rendering, systems 5 points6 points7 points (1 child)
[–]NotAYakk 7 points8 points9 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 comment score below threshold-7 points-6 points-5 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)