This article covers the use of functions in C++ (version 17), as well as some keywords related to them.
int addition (int a, int b) {...}
. If a function is to not return any value, the output type is specified as void
. They can be declared without function body and input variable names early into the code, following up with the actual definition later on, too. For this, the curly braces have to be omitted and a semicolon is added at the end. Doing this allows the program to know the functions in advance, which may be necessary when there are function dependencies (functions calling other functions), or to improve readability. Example:
int foo1(int); //Declare without implementation (and input variable names). int foo2(int); //This is necessary, since both functions potentially call each other. int foo1(int a) { //Doubles positive integers. If negative, pass to foo2. if (a > 0) { return 2 * a; } else { return foo2(a); } //foo2 needs to be declared at this point. } int foo2(int a) { //Doubles negative integers and changes sign. If positive, pass to foo1. if (a <= 0) { return (-2)*a; } else { return foo1(a); } //foo1 already declared (and implemented). }We can add default values to the input variables, by adding an equal sign and the desired value/variable to arguments at the end of the input variable list. They don't have to be constants and can be redeclared, but only in differing scopes.
#include <iostream> int var1 = 4; int var2 = 5; int sum(int a = var1, int b = var2) { //Declare defaults and implement function. return a + b; } // int sum(int = 12, int = 3); //Wouldn't compile due to redeclaration in the same scope. int main() { std::cout << "sum(): " << sum() << ".\n"; //Prints var1+var2. std::cout << "sum(11): " << sum(11) << ".\n"; //Prints 11+var2. std::cout << "sum(11,12): " << sum(11,12) << ".\n"; //Prints 11+12. var2 = 13; //Default values can be redeclared through variables. // int sum(int = var1, int = 13); //Would work, too, since it's a different scope. //The second input argument would be unchangeable //for the rest of the scope, though. std::cout << "sum(20) with new var2: " << sum(20) << ".\n"; return 0; }Example output:
sum(): 9. sum(11): 16. sum(11,12): 23. sum(20) with new var2: 33.
sum(11)
, which translates to sum(11, var2)
. However, there is no shortcut for replacing var2
and keeping var1
as default, i.e. sum(var1, 11)
is not possible without explicitly typing both arguments.
&
) after the input type in the declaration. Note that calling by reference is similar to using pointers, but has some distinctions. Most notably, references always lead to the memory address of a valid variable (in fact, the reference has the same address as the original), while pointers can point to any address, potentially causing undefined behavior. Thus, references are usually used instead of pointers as function input.int sum(int a, int b) { //Small sized input and original variables are to remain unchanged => Call by value. return a + b; } void triple(int &a) { //Function is to triple input => Call by reference. a *= 3; } void triple_p(int *a) { //Alternative: Call by pointer. *a *= 3; }
int sum(int a, int b) { return a + b; } int sum(int a, int b, int c) { return a + b + c; }
template <class identifier1, class identifier2>
(for two different template types) before a function declaration. Instead of the keyword class
, typename
can be used, too. After that, the template can be used like any other data type.#include <iostream> template <class T> T sum(T a, T b) { //Sum of two input arguments of generic type T. return a + b; //+ operator has to be implemented for T. } template <typename T1, typename T2> //Two template types. Use of "typename" equivalent to "class". bool bigger_size() { //Function doesn't need input arguments to work. return sizeof(T1) > sizeof(T2); //Checks, whether T1 type variables use more memory space than T2. } //Returns true (1) if bigger and false (0) if smaller or equal. int main() { int a, b; a = b = 2; double c, d; c = 3.14; d = 1.234; int res1 = sum<int>(a, b); //Instantiates function sum with template parameter T replaced by int. std::cout << res1 << "\n"; double res2 = sum<double>(c, d); //Instantiates sum with double type input. std::cout << res2 << "\n"; bool res3 = bigger_size<int, double>(); //int bigger than double? (no) std::cout << res3 << "\n"; bool res4 = bigger_size<float, char>(); //float bigger than char? (yes) std::cout << res4; return 0; }Example output:
4 4.374 0 1
template <>
written before the declaration and the template data type is specified after the function name, e.g. int sum<int>(int a, int b)
. The specialized function has to be declared in the same scope as the original template (for member functions, it has to be in the class/struct scope).#include <iostream> template <typename T1, typename T2> //Same function as in example above. bool bigger_size() { return sizeof(T1) > sizeof(T2); //T1 using more memory than T2? } template <> //Explicit template specialization. bool bigger_size<short, double>() { //All generic arguments are specified. std::cout << "(specialization used)"; return 0; //short probably never bigger than double. } //Partial template specialization not allowed for functions! //Also: Overloading not possible here, without adding somewhat redundant input arguments. /* template <class T2> bool bigger_size<char, T2>() { return 0; //Since char only of size 1, it's never bigger than any other data type. } */ int main() { std::cout << bigger_size<short, char>() << "\n"; //Call standard template. std::cout << bigger_size<short, double>(); //Call explicit specialization. return 0; }Example Output:
1 (specialization used)0
#include <iostream> template <class T, int N> //One generic and one fixed data type. int sum(T a) { return a + N; //There may be problems with type conversion if T not int. } int main() { std::cout << sum<int, 5>(7); //Instantiates a sum function that adds 5 to input. //Output: 12. return 0; }
inline
for this reason has become mostly obsolete and is only used (if at all) at small functions that are called frequently. To declare an inline function, the keyword inline
is added before the function declaration.//Header file could be included in multiple translation units, //since function is marked as inline. inline int sum(int a, int b) { return a + b; } //Declare some function. No inline required, because no implementation here. int do_sum(int a, int b);do_sum.cpp
//Define sum. Could've included the header file instead. inline int sum(int a, int b) { return a + b; } //We could've defined sum differently than in the header, //with no error, when jointly compiled! /* inline int sum(int a, int b) { return 1000 + a + b; } */ //Implement the function from the header file. //Redundant function that calls sum. int do_sum(int a, int b) { return sum(a, b); }main.cpp
#include <iostream> #include "header.h" int main() { std::cout << sum(1, 4) << "\n"; //Calls function from header OR do_sum in joint compilation. //Which one is called, depends on compiling order. std::cout << do_sum(1, 19); //Works, due to joint compilation. //do_sum also had to be declared here (or in the header). return 0; }Compile .cpp files jointly. Output:
5 20Note: If we had implemented the alternative
sum
function in do_sum.cpp, the compiling order could affect the outcome! With g++ compiler, g++ do_sum.cpp main.cpp uses the sum function from do_sum.cpp, while g++ main.cpp do_sum.cpp were to use the one from the header file.