Introduction to Functions

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

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.

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

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
}

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;
}

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)
Next Lesson
Lesson 4 of 129

Namespaces, Includes, and the Standard Library

A quick introduction to namespaces in C++, alongside the standard library and how we can access it

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

When to Use Default Arguments in C++ Functions
In what situations is it beneficial to use default arguments for function parameters?
Passing Arguments by Value vs Reference in C++
What is the difference between passing function arguments by value and by reference?
Best Practices for Function Overloading in C++
What are some best practices to follow when overloading functions in C++?
When to Use Forward Declarations in C++
In what situations should I use forward declarations in my C++ code?
Pros and Cons of Global Variables in C++
What are the advantages and disadvantages of using global variables in C++?
Returning by Value vs Reference in C++
When should I return by value from a function, and when should I return by reference?
Or Ask your Own Question
Get an immediate answer to your specific question using our AI assistant