Introduction to Functions

Learn the basics of writing and using functions in C++, including syntax, parameters, return types, and scope rules.
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, Unlimited Access
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Updated

This lesson is a quick introductory tour of functions within C++. It is not intended for those who are entirely new to programming. Rather, the people who may find it useful include:

  • those who have completed our introductory course, but want a quick review
  • those who are already familiar with programming in another language, but are new to C++
  • those who have used C++ in the past, but would benefit from a refresher

It summarises several lessons from our introductory course. Those looking for more thorough explanations or additional context should consider completing Chapter 2 of that course.

Previous Course

Intro to Programming with C++

Starting from the fundamentals, become a C++ software engineer, step by step.

Screenshot from Cyberpunk 2077
Screenshot from The Witcher 3: Wild Hunt

Function Syntax

In C++, a function that is called MyFunction that returns void (ie, nothing) and does nothing looks like this:

void MyFunction() {}

Here are examples of functions that return data. Note that we specify the return type in the function heading:

float ReturnFloat() {
  return 5.5f;
}

bool ReturnBool() {
  return true;
}

Similar to variables, return statements can be implicitly cast to the return type of the function:

// This returns the integer 5
int ReturnInt() {
  return 5.5f;
}

We invoke functions using their name, and the () operator:

ReturnFloat();

The main Function

Our program needs to define an entry point - the function that will be run when our program is executed.

This is typically a function that returns an int and is called main:

int main() {
  return 0;
}

The return value of the main function is an exit code, which describes why our program stopped. Returning 0 indicates the program ran as expected. Any other number is an error code.

If the main function returns nothing, it is assumed to have returned 0. This is a behavior that is specific just to the main function. Every other function must return something of the type specified in its heading, or something that can be converted to that type.

// This is valid
int main() {}

// This is not valid
int SomeFunction() {}

Function Parameters and Arguments

We define parameters between the () in the function definition. We specify their type and name, and separate multiple parameters using commas. Below, we declare our function has two parameters, both of type int, which we’re calling x and y:

int Add(int x, int y) {
  return x + y;
}

We provide arguments for these parameters between the ( and ) when calling the function. Below, we pass 1 to x and 2 to y:

int main() {
  Add(1, 2); // 3
}

Arguments vs Parameters

A function’s arguments are the values we pass to a function when we call it.

A function’s parameters are the variables that receive those values, which we defined alongside the function’s name

These terms are sometimes used interchangably, when the meaning is clear from the context.

We can define default values for parameters, making them optional. We do this using the = token within the function definition:

int Add(int x = 1, int y = 2) {
  return x + y;
}

We can pass {} as an argument to use the default value in that position, or omit it entirely if it’s at the end of our argument list:

int Add(int x = 1, int y = 2) {
  return x + y;
}

int main() {
  // Use no defaults
  Add(3, 4); // 7

  // Use default for 1st parameter
  Add({}, 5); // 6

  // Use default for 2nd parameter
  Add(2, {}); // 4
  Add(2); // 4

  // Use default for both parameters
  Add({}, {}); // 3
  Add({}); // 3
  Add(); // 3
}

Function return types can be set to auto which will ask the compiler to infer what it should be based on the return statements:

// Compiler deduces it will return an int
auto Add(int x, int y) {
  return x + y;
}

auto Function Parameters

Function parameters can also have a type of auto, but this doesn’t mean what we might expect.

Using auto in a function’s parameter list creates a function template. We explain templates in detail later in this course.

Implicit Argument Conversion

Like with variables, the compiler will attempt to implicitly convert function arguments and return values into the appropriate type. It will only present an error if it cannot do the conversion.

// Will return 2
int GetInt() {
  return 2.5f;
}

int Add(int x, int y) {
  return x + y;
}
// All expressions will return 2
// they are equivalent to Add(2, 2)
Add(2, 2);
Add(2.5f, 2.5f);
Add(2, 2.5f);
Add(2.5, 2.5);

Function Overloading

It’s possible to have two functions with the same name in the same scope.

The way the compiler determines which function we want to call is by comparing the argument types to the parameter types:

// Integer Overload
int Add(int x, int y) {
  return x + y;
}

// Float Overload
float Add(float x, float y) {
  return x + y;
}

Add(2, 2); // Calling Integer Overload
Add(2.5f, 2.5f); // Calling Float Overload

If it is ambiguous what function we want to call, the compiler will throw an error. The following is ambiguous because the compiler doesn’t know which variation of Add to use:

Add(2, 2.5f);

It could convert the first argument to a float, and call the overloads that accept a (float, float) argument list. Or, it could convert the second argument to an int, and call the (int, int) overload.

It doesn’t know what we want, so it throws an error.

error: 'Add': overloaded functions have similar conversions
could be 'float Add(float,float)'
or       'int Add(int,int)'
trying to match argument list '(int, float)'

The following also throws an error, as we provide two double arguments. A double can be converted to either a float or an int so again, we’re being ambiguous:

Add(2.5, 2.5);
error: 'Add': ambiguous call to overloaded function
could be 'float Add(float,float)'
or       'int Add(int,int)'
trying to match argument list '(double, double)'

We can remove the ambiguity by explicitly casting our arguments to the desired type.

// Calling (int, int) Overload
Add(2, static_cast<int>(2.5f));

// Calling (float, float) Overload
Add(
  static_cast<float>(2.5),
  static_cast<float>(2.5)
);

Forward Declaration

Functions can be declared and defined as separate statements. A function declaration looks the same as a definition, but without the body:

int Add(int x, int y);

A function’s return type, name, and parameter types are often called the function prototype or function signature.

Function prototypes technically don’t need to name their parameters:

int Add(int, int);

But it’s common to include the names, to remind ourselves (or other developers reading our code) what the parameters are for.

Function declarations allow our functions to be called even if they are not yet defined at the place where they are called.

This is referred to as a forward declaration:

int Add(int x, int y);

int main() {
  Add(1, 2);
}

int Add(int x, int y) {
  return x + y;
}

The difference between a declaration and a definition will become more important as we advance through this course.

Global and Local Scope

We don’t necessarily have access to all of our functions and variables from anywhere in our program.

Scopes define which variables are accessible, and from where. Scopes are typically created any time we have a set of braces, { and }

For example, a function body creates a scope.

Expressions can access their own scope, and any ancestor (parent, grandparent, etc) scope:

// Global scope
int GlobalInt{0};

void MyFunction() {
  // Local scope
  int LocalInt{0};

  // This is fine - we're accessing
  // a variable in the parent scope
  GlobalInt++;
}

But they cannot access any descendant (child, grandchild, etc) scopes:

void MyFunction() {
  int LocalInt{0};

  {
    // Fine - LocalInt is in a parent scope
    ++LocalInt;
    int ChildInt{1};
  }

  // Error - we don't have access to child scope
  ChildInt++;
}

Summary

In this lesson, we've covered the fundamentals of functions. We've seen how to define functions, call them, and control their visibility using scopes. Key takeaways:

  • Functions are defined with a return type, name, and parameter list
  • Functions can be called using their name and providing arguments
  • Functions can be overloaded by providing different parameter types
  • Forward declarations allow functions to be used before they are defined
  • Variables can be global (accessible everywhere) or local (accessible only within a specific scope)

Was this lesson useful?

Next Lesson

Namespaces, Includes, and the Standard Library

A quick introduction to namespaces in C++, alongside the standard library and how we can access it
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Updated
Lesson Contents

Introduction to Functions

Learn the basics of writing and using functions in C++, including syntax, parameters, return types, and scope rules.

sdl2-promo.jpg
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, Unlimited Access
  • 53.GPUs and Rasterization
  • 54.SDL Renderers
sdl2-promo.jpg
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, unlimited access

This course includes:

  • 55 Lessons
  • 100+ Code Samples
  • 91% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Namespaces, Includes, and the Standard Library

A quick introduction to namespaces in C++, alongside the standard library and how we can access it
Abstract art representing computer programming
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved