-
Notifications
You must be signed in to change notification settings - Fork 82
Functions
Hannes Hauswedell edited this page Apr 12, 2017
·
1 revision
We distinguish between
- member functions of a class or struct, also called methods
- free functions in the scope of namespace (not class/struct), also called global functions
First read the Chapter on Functions in the CoreGuidelines!
Basics:
- in-parameters are parameters that are only read from in the function
- shall be
type variable
(copy) if you want a copy inside function - shall be
type const variable
for small builtins, i.e. mainly arithmetic types - shall be
type const & variable
for specific class types - shall be
type && variable
for parameters with type deduction (templated parameters) - shall be
type const & /**/
for type-only parameters / tags
- shall be
- in-out-parameters are parameters that are both read from, and written to
- shall be
&
in all cases
- shall be
- out-parameters are parameters that are only written to
- shall be return values (if multiple return values, put them in
std::tuple<>
) - in case you need to specialize over the type of the out-parameter, treat it as in-out
- shall be return values (if multiple return values, put them in
Reasons:
- in-parameters:
- If you plan on copying the argument inside your function, you should instead copy it in the signature, because this enables usage of the move-constructor, saving the copy operation if the function is passed a temporary. It also eases writing exception-safe code. [See also the copy and swap idiom]
- The specified types are smaller than references.
- Don't copy, since you don't have to; use
const
protection, because you can. - Since the type before the
&&
is subject to type deduction, the type is not an rvalue reference, but a forwarding reference. This implies that it can resolve to&
,&&
and alsoconst &
so it is more generic than onlyconst &
. This is especially important for objects that are notconst
-iterable like certain ranges. - If you are not going to use an argument's value, omit the variable to enable compiler optimization.
- out-parameters
- Since C++17 there is guaranteed copy elision on return values so we don't need to worry about it and just return. There are also so called structured bindings to easily access the return values.
Number of arguments:
- should be β€ 5
- use ranges instead of individual begin + end iterators (if applicable)
- use
std::pair
s andstd::tuple
s instead of individual value parameters (if applicable) - use traits instead of individual type parameters (if applicable)
There is no strict policy on the order of arguments (e.g. "output before input"), use the following guideline:
- "data arguments" (e.g. a string that is being processed)
- "input data arguments" come before "output data arguments" (but often the latter are return values anyway)
- "option arguments" (e.g. how the string shall be processed)
- type-only "option arguments" (e.g. tags or traits)
Also keep in mind that if you want to default some parameters, they need to be at the end. Execution policies are a special case and always come first.
Design your function signature so that there aren't too many possible interfaces, ideally 1-2, but not more than 3-5 (with and without defaults).
- always constrain your template parameters!
- choose the least-constrained concept that works for your algorithm
- but enforce all the requirements that you actually have!
- use forwarding reference
&&
instead ofconst &
, also for read only parameters (see above)
Examples for 2.:
- do not require a
container_concept
if aforward_range_concept
is sufficient - do not require a
random_access_sequence_concept
if asequence_concept
is sufficient
Examples for 3.:
- if you do require random access, make sure that you include the corresponding requirements!
TODO?
TODO?