Functions (Basics)

Return Types

Full Return Type Deduction C++14

(deduction = compiler determines type automatically)

auto foo (int i, double d) {

return i;
}
// OK: return type: int
auto foo (int i, double d) {
return i; // int

return d; // double
}
// ERROR: Inconsistent return types!

Parameters

Default Parameters

double f (double a, double b = 1.5) {
return (a * b);
}
int main () {
cout << f(2); // 1 argument → 3.0
cout << f(2, 3); // 2 arguments → 6.0
}
void foo (int i = 0);
void foo (int n, double x = 2.5);
void foo (int a, int b = 1, float c = 3.5f);
void foo (int a, int b = 1, int c ); // ERROR!
// Each parameter after first default must have default value, too!

Overloading

  • functions with the same name but different parameter lists
  • cannot overload on return type alone

✔same name, different parameter lists

int abs (int i) {
return ((i < 0) ? -i : i);
}

double abs (double d) {
return ((d < 0.0) ? -d : d);
}
int a = -5;
double b = -2.23;
auto x = abs(a); // int abs(int)
auto y = abs(b); // double abs(double)

❌ same name, same parameter lists

int foo (int i) { ❌
return (2 * i);
}

double foo (int i) { ❌
return (2.5 * i);
}
// DOES NOT COMPILE!

Declaration vs. Definition

  • can only call functions that are already known (from before/above)
  • only one definition allowed per source file (translation unit)
  • ok to have any number of declarations = announcing the existence of a function by specifying its signature

Contracts

When designing a function, think about:

  • Preconditions: What do you expect/demand from input values?
  • Postconditions: What guarantees should you give regarding output values?
  • Invariants: What do callers/users of your function expect to not change?
  • Purpose: Has your function a clearly defined purpose?
  • Name: Does the function’s name reflect its purpose?
  • Parameters: Can a caller/user easily confuse their meaning?
Precondition Checks

Wide Contract Functions perform precondition checks, i.e., check input parameter values (or program state) for validity

Narrow Contract Functions do not perform precondition checks, i.e., the caller has to make sure that input arguments (and program state) are valid

Attribute [[nodiscard]] C++17

encourages compilers to issue warnings if function return values are discarded

[[nodiscard]] bool prime (int i) { … }
// return value(s) used:
bool const yes = prime(47);
if (prime(47)) { … }
// return value discarded/ignored:
prime(47); // ⚠ COMPILER WARNING

Example from the standard library:

std::vector‘s empty() function is declared with [[nodiscard]] as of C++20, because it can be confused with clear():

std::vector<int> v;
// …
if (v.empty()) { … } // OK
v.empty(); // C++20: ⚠ COMPILER WARNING
// oops … did someone meant to clear it?

Declare your function return values [[nodiscard]]

  • if calling it without using the return value makes no sense in any situation
  • if users could be confused about its purpose, if the return value is ignored

No-Throw Guarantee: noexcept C++11

C++ has a mechanism for reporting errors using exceptions like many/most other programming languages. Don’t worry, if you don’t know what exceptions are, they will be explained in a later chapter.

The noexcept keyword specifies that a function promises to never throw exceptions / let exceptions escape:

void foo () noexcept { … }

If an exception escapes from a noexcept function anyway, the program will be aborted.

Some Mathematical Functions

#include <cmath>
double sqrt (double x) square root
double pow (double a, double b) power
double abs (double x) absolute value
double sin (double x) sine
double cos (double x) cosine
double exp (double x) exponential
double log (double x) logarithm
double floor (double x) ⌊x⌋ next smaller integer
double ceil (double x) ⌈x⌉ next larger integer
double fmod (double x, double y) remainder of x/y

References

https://hackingcpp.com/cpp/lang/function_basics.html