From 38a6acc1c09376b80f760aa6ca284f1873a1fdec Mon Sep 17 00:00:00 2001 From: hsutter Date: Mon, 20 Mar 2017 11:35:29 -0700 Subject: [PATCH 1/2] Closes #827 Stated the main two reasons why functions with lots of parameters exist: Because they're missing an abstraction, or because the function is trying to do more than one job. Added explicit coverage of those reasons and another example illustrating that. --- CppCoreGuidelines.md | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index f1a44b4..337a211 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -1864,15 +1864,25 @@ It is usually best to avoid global (namespace scope) objects altogether. Having many arguments opens opportunities for confusion. Passing lots of arguments is often costly compared to alternatives. +##### Discussion + +The two most common reasons why functions have too many parameters are: + + 1. *Missing an abstraction.* There is an abstraction missing, so that a compound value is being passed as individual elements instead of as a single object that enforces an invariant. This not only expands the parameter list, but it leads to errors because the component values are no longer protected by an enforced invariant. + + 2. *Violating "one function, one responsibility."* The function is trying to do more than one job and should probably be refactored. + ##### Example -The standard-library `merge()` is at the limit of what we can comfortably handle +The standard-library `merge()` is at the limit of what we can comfortably handle: template OutputIterator merge(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp); +Note that this is because of problem 1 above -- missing abstraction. Instead of passing a range (abstraction), STL passed iterator pairs (unencapsulated component values). + Here, we have four template arguments and six function arguments. To simplify the most frequent and simplest uses, the comparison argument can be defaulted to `<`: @@ -1894,12 +1904,24 @@ Alternatively, we could use concepts (as defined by the ISO TS) to define the no Mergeable{In1 In2, Out} OutputIterator merge(In1 r1, In2 r2, Out result); +##### Example + +The safety Profiles recommend replacing + + void f(int* some_ints, int some_ints_length); // BAD: C style, unsafe + +with + + void f(gsl::span some_ints); // GOOD: safe, bounds-checked + +Here, using an abstraction has safety and robustness benefits, and naturally also reduces the number of parameters. + ##### Note -How many arguments are too many? Try to use less than Four arguments. -There are functions that are best expressed with four individual arguments, but not many. +How many parameters are too many? Try to use fewer than four (4) parameters. +There are functions that are best expressed with four individual parameters, but not many. -**Alternative**: Group arguments into meaningful objects and pass the objects (by value or by reference). +**Alternative**: Use better abstraction: Group arguments into meaningful objects and pass the objects (by value or by reference). **Alternative**: Use default arguments or overloads to allow the most common forms of calls to be done with fewer arguments. From 33098ab31ee429eb0d42ece1b219c6685da99759 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 20 Mar 2017 11:44:13 -0700 Subject: [PATCH 2/2] Updated guidance on noexcept on destructors (#814) --- CppCoreGuidelines.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 337a211..aa88a77 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -4573,7 +4573,7 @@ If a destructor uses operations that may fail, it can catch exceptions and in so ##### Enforcement -(Simple) A destructor should be declared `noexcept`. +(Simple) A destructor should be declared `noexcept` if it could throw. ### C.37: Make destructors `noexcept` @@ -4583,11 +4583,11 @@ If a destructor uses operations that may fail, it can catch exceptions and in so ##### Note -A destructor (either user-defined or compiler-generated) is implicitly declared `noexcept` (independently of what code is in its body) if all of the members of its class have `noexcept` destructors. +A destructor (either user-defined or compiler-generated) is implicitly declared `noexcept` (independently of what code is in its body) if all of the members of its class have `noexcept` destructors. By explicitly marking destructors `noexcept`, an author guards against the destructor becoming implicitly `noexcept(false)` through the addition or modification of a class member. ##### Enforcement -(Simple) A destructor should be declared `noexcept`. +(Simple) A destructor should be declared `noexcept` if it could throw. ## C.ctor: Constructors