type name ( parameter1, parameter2, ...) { statements }
Ordering
-
Functions cannot be called before they are declared. In the examples, the functions were always defined before the main function, which is the function from where the other functions were called. If main were defined before the other functions, this would break the rule that functions shall be declared before being used, and thus would not compile.
Prototyping
-
The prototype of a function can be declared without actually defining the function completely, giving just enough details to allow the types involved in a function call to be known. Naturally, the function shall be defined somewhere else, like later in the code. But at least, once declared like this, it can already be called.
-
The declaration shall include all types involved (the return type and the type of its arguments), using the same syntax as used in the definition of the function, but replacing the body of the function (the block of statements) with an ending semicolon.
-
The parameter list does not need to include the parameter names, but only their types.
-
Parameter names can nevertheless be specified, but they are optional, and do not need to necessarily match those in the function definition.
-
Anyway, including a name for each parameter always improves legibility of the declaration.
-
int proto_function (int first, int second);
int proto_function (int, int);
#include <iostream>
void odd (int x);
void even (int x);
int main() {
int i;
do {
std::cout << "Please, enter number (0 to exit): ";
std::cin >> i;
odd(i);
} while (i!=0);
return 0;
}
void odd (int x) {
if ((x%2)!=0) std::cout << "It is odd.\n";
else even (x);
}
void even (int x) {
if ((x%2)==0) std::cout << "It is even.\n";
else odd (x);
}
Returns
Empty Return
-
In C++, an empty parameter list can be used instead of void with same meaning, but the use of void in the argument list was popularized by the C language, where this is a requirement.
void print_message() {
std::cout << "I'm a function!";
}
-
Is the same as:
void print_message(void) {
std::cout << "I'm a function!";
}
For function
main
-
If the execution of main ends normally without encountering a return statement the compiler assumes the function ends with an implicit return statement:
return 0;
-
This only applies to function main for historical reasons.
-
When main returns zero (either implicitly or explicitly), it is interpreted by the environment as that the program ended successfully. Other values may be returned by main, and some environments give access to that value to the caller in some way, although this behavior is not required nor necessarily portable between platforms.
-
The values for main that are guaranteed to be interpreted in the same way on all platforms are:
-
0-
The program was successful
-
-
EXIT_SUCCESS-
The program was successful (same as above).
-
This value is defined in header
<cstdlib>.
-
-
EXIT_FAILURE-
The program failed.
-
This value is defined in header
<cstdlib>.
-
-
Parameters by value or by reference
-
By value:
void duplicate (int a, int b, int c)
-
By reference:
void duplicate (int& a, int& b, int& c)
-
Calling a function with parameters taken by value causes copies of the values to be made. The function operates directly on (aliases of) the strings passed as arguments, and, at most, it might mean the transfer of certain pointers to the function.
-
This is a relatively inexpensive operation for fundamental types such as int, but if the parameter is of a large compound type, it may result on certain overhead
string concatenate (string a, string b) {
return a+b;
}
-
If these are long strings, it may mean copying large quantities of data just for the function call.
-
But this copy can be avoided altogether if both parameters are made references:
string concatenate (string& a, string& b) {
return a+b;
}
Const Reference
-
On the flip side, functions with reference parameters are generally perceived as functions that modify the arguments passed, because that is why reference parameters are actually for.
-
The solution is for the function to guarantee that its reference parameters are not going to be modified by this function. This can be done by qualifying the parameters as constant:
string concatenate (const string& a, const string& b) {
return a+b;
}
-
By qualifying them as const, the function is forbidden to modify the values of neither
anorb, but can actually access their values as references (aliases of the arguments), without having to make actual copies of the strings. -
Therefore,
constreferences provide functionality similar to passing arguments by value, but with an increased efficiency for parameters of large types . -
Note though, that for most fundamental types, there is no noticeable difference in efficiency, and in some cases, const references may even be less efficient! .
inline (C99, C++98)
-
Calling a function generally causes a certain overhead (stacking arguments, jumps, etc...), and thus for very short functions, it may be more efficient to simply insert the code of the function where it is called, instead of performing the process of formally calling a function.
-
Preceding a function declaration with the inline specifier informs the compiler that inline expansion is preferred over the usual function call mechanism for a specific function. This does not change at all the behavior of a function, but is merely used to suggest the compiler that the code generated by the function body shall be inserted at each point the function is called, instead of being invoked with a regular function call.
inline string concatenate (const string& a, const string& b) {
return a+b;
}
-
Note that most compilers already optimize code to generate inline functions when they see an opportunity to improve efficiency, even if not explicitly marked with the inline specifier. Therefore, this specifier merely indicates the compiler that inline is preferred for this function, although the compiler is free to not inline it, and optimize otherwise. In C++, optimization is a task delegated to the compiler, which is free to generate any code for as long as the resulting behavior is the one specified by the code.
-
Modern compilers ignore it as an inlining hint if they choose.
-
inlinerarely changes performance today.
In C
-
It interacts with:
-
extern -
internal vs external linkage
-
definition emission
-
-
inline alone:
-
provides an inline definition
-
does NOT emit a standalone external definition
-
requires one external definition elsewhere
-
In C++
-
inline mainly changes linkage rules, not performance.
// header.hpp
inline int add(int a, int b) {
return a + b;
}
-
Every translation unit including this header can define it.
-
Without inline, this would violate the One Definition Rule.
-
inline in C++ enables:
-
header-defined functions
-
template-like function placement
-
header-only libraries
-
-
It is fundamentally a linkage mechanism
Alternatives
-
GCC / Clang:
-
__attribute__((always_inline))
-
-
MSVC:
-
__forceinline
-
-
Those are stronger hints ( still not absolute guarantees ).
Default Parameters (C++98)
-
This is not supported in C.
#include <iostream>
int divide (int a, int b=2) {
int r;
r=a/b;
return (r);
}
int main () {
std::cout << divide (12) << '\n';
std::cout << divide (20,4) << '\n';
return 0;
}
Overloaded Functions
#include <iostream>
int operate(int a, int b) {
return (a*b);
}
double operate(double a, double b) {
return (a/b);
}
int main() {
int x=5,y=2;
double n=5.0,m=2.0;
std::cout << operate (x,y) << '\n';
std::cout << operate (n,m) << '\n';
return 0;
}
Templates
-
Syntax:
template <template-parameters> function-declaration
template <class SomeType>
SomeType sum (SomeType a, SomeType b) {
return a+b;
}
-
It makes no difference whether the generic type is specified with keyword class or keyword typename in the template argument list (they are 100% synonyms in template declarations).
-
In the code above, declaring SomeType (a generic type within the template parameters enclosed in angle-brackets) allows SomeType to be used anywhere in the function definition, just as any other type; it can be used as the type for parameters, as return type, or to declare new variables of this type. In all cases, it represents a generic type that will be determined on the moment the template is instantiated.
-
Instantiating a template is applying the template to create a function using particular types or values for its template parameters. This is done by calling the function template, with the same syntax as calling a regular function, but specifying the template arguments enclosed in angle brackets
name <template-arguments> (function-arguments)
-
Example:
template <class SomeType> SomeType sum (SomeType a, SomeType b) { return a+b; } x = sum<int>(10,20);-
Results in:
int sum(int a, int b) { return a+b; } -
-
The template parameters can not only include types introduced by class or typename, but can also include expressions of a particular type:
#include <iostream>
template <class T, int N>
T fixed_multiply (T val) {
return val * N;
}
int main() {
std::cout << fixed_multiply<int,2>(10) << '\n';
std::cout << fixed_multiply<int,3>(10) << '\n';
}
-
The second argument of the fixed_multiply function template is of type int. It just looks like a regular function parameter, and can actually be used just like one.
-
But there exists a major difference: the value of template parameters is determined on compile-time to generate a different instantiation of the function fixed_multiply, and thus the value of that argument is never passed during runtime: The two calls to
fixed_multiplyin main essentially call two versions of the function: one that always multiplies by two, and one that always multiplies by three. For that same reason, the second template argument needs to be a constant expression (it cannot be passed a variable) .
Function Pointers
-
Pointers to functions are declared with the same syntax as a regular function declaration, except that the name of the function is enclosed between parentheses () and an asterisk (*) is inserted before the name:
#include <iostream>
int addition(int a, int b) {
return (a+b);
}
int subtraction(int a, int b) {
return (a-b);
}
int operation(int x, int y, int (*func_to_call)(int,int)) {
int g;
g = (*func_to_call)(x,y);
return (g);
}
int main() {
int m,n;
int (*minus)(int,int) = subtraction;
m = operation(7, 5, addition);
n = operation(20, m, minus);
std::cout <<n;
return 0;
}