This lesson is still being designed and assembled (Pre-Alpha version)

Functions

Overview

Teaching: 10 min
Exercises: 0 min
Questions
  • How to define a function ?

  • What are the different ways to pass input arguments ?

  • What are the different ways to get back the results ?

Objectives
  • Know about ordinary C++ functions.

  • Know about references and const references.

  • Be aware of return value optimization and structured bindings.

Different examples of input arguments and output results

With return type

int square(int a) {
  return a*a;
}

Multiple parameters

int mult(int a, int b) {
  return a*b;
 }

No parameter

void hello() {
  std::cout << "Hello World" << std::endl;
}

No return

void log(char* msg) {
  std::cout << msg << std::endl;
}

Different ways to exchange arguments

By value

Each time the function is called, the value given as argument is duplicated within the function. If the function modify the argument, only the internal copy is modified, not the original (which is often what we want). The duplication may take time when the input argument is big.

void display( std::vector<int> col )
 {
  std::cout << "[ " ;
  for ( unsigned i = 0 ; i < col.size() ; ++i )
   { std::cout << col[i] << " " ; }
  std::cout << "]" << std::endl ;
 }

int main()
 {
  std::vector col = { 1, 2, 3, 4, 5 } ;
  display(col) ;
 }
Get the full file: code/Functions/arg-by-value.cpp

By reference

If you want the function to modify the original value, you must declare the argument as a reference (postfix with &).

void scale( std::vector<int> & col, int factor )
 {
  for ( unsigned i = 0 ; i < col.size() ; ++i )
   { col[i] *= factor ; }
 }
Get the full file: code/Functions/arg-by-reference.cpp

By constant reference

If you do want the function to modify the original value, but you would like to avoid the cost of the copy, you can declare the argument as a constant reference (prefix with const and postfix with &).

void display( const std::vector<int> & col )
 {
  std::cout << "[ " ;
  for ( unsigned i = 0 ; i < col.size() ; ++i )
   { std::cout << col[i] << " " ; }
  std::cout << "]" << std::endl ;
 }
Get the full file: code/Functions/arg-by-const-reference.cpp

This pratice is not worth for small builtin types such as int, double, or the standard libray iterators, which are usually passed by value.

Different ways to return results

By value… and only by value !

We have seen that one can pass a variable as reference to a function, and the function modify it : it was the old way to proceed when you have several results, or big ones you want to avoid to duplicate.

Nowadays, whenever you can, simply return the result by value, as would do a mathematical function.

std::vector<int> scale( std::vector<int> col, int factor )
 {
  for ( unsigned i = 0 ; i < col.size() ; ++i )
   { col[i] *= factor ; }
  return col ;
 }
Get the full file: code/Functions/return-by-value.cpp

Do not be afraid of returning a big value, object, array, etc. Most of the time, if not every time, the compiler will avoid the copy and directly write the result in the client memory area. This is called RVO (Return Value Optimization).

NEVER return a reference, unless you are a C++ great master !

Returning a composite result

Even if you have multiple results, it is more and more easy to return them all together, using a std::tuple.

std::tuple<double,double,double> analyse( const std::vector<double> & data )
 {
  double min {999.}, max{0}, sum {0} ;
  for ( unsigned i = 0 ; i < data.size() ; ++i )
   {
    sum += data[i] ;
    if (data[i]<min ) min = data[i] ;
    if (data[i]>max ) max = data[i] ;
   }
  return { min, max, sum/data.size() } ;
 }

int main()
 {
  std::vector<double> data = { 1., 2., 3., 4., 5., 6. } ;

  double min, max, mean ;
  std::tie(min,max,mean) = analyse(data) ;

  std::cout << "min : " << min << std::endl ;
  std::cout << "max : " << max << std::endl ;
  std::cout << "mean: " << mean << std::endl ;
 }
Get the full file: code/Functions/return-tuple.cpp

The example above will be even simpler when we will introduce auto later on.

Key Points

  • Const references avoid the cost of the copy for input arguments.

  • You should not be afraid any more of returning big results.

  • You should not be afraid any more of returning a bunch of results.