Declaring functions

< cpp‎ | language
Revision as of 20:56, 23 July 2013 by Cubbi (Talk | contribs)

C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Function declaration
Lambda function declaration
inline specifier
Exception specifications (deprecated)
noexcept specifier (C++11)
decltype (C++11)
auto (C++11)
alignas (C++11)
Storage duration specifiers
Alternative representations
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Attributes (C++11)
typedef declaration
Type alias declaration (C++11)
Implicit conversions - Explicit conversions
static_cast - dynamic_cast
const_cast - reinterpret_cast
Memory allocation
Class-specific function properties
Special member functions

A function declaration introduces the function name and its type. A function definition associates the function name/type with the function body.


Function declaration

Function declarations may appear in any scope. A function declaration at class scope introduces a class member function (unless the friend specifier is used), see member functions and friend functions for details.

The type of the function being declared is composed from the return type (provided by the decl-specifier-seq of the declaration syntax and the function declarator

noptr-declarator ( parameter-list ) cv(optional) ref(optional) except(optional) attr(optional) (1)
noptr-declarator ( parameter-list ) cv(optional) ref(optional) except(optional) attr(optional) -> trailing (2) (since C++11)

(see Declarations for the other forms of the declarator syntax)

1) Regular function declarator syntax
2) Trailing return type declaration: trailing return type is only allowed on the outermost function declarator. The decl-specifier-seq in this case must contain the keyword auto
noptr-declarator - any valid declarator, but if it begins with *, &, or &&, it has to be surrounded by parentheses.
parameter-list - possibly empty, comma-separated list of the function parameters (see below for details)
attr(C++11) - optional list of attributes
cv - const/volatile qualification, only allowed in non-static member function declarations
ref(C++11) - ref-qualification, only allowed in non-static member function declarations
except - either dynamic exception specification(deprecated) or noexcept specification(C++11)
trailing(C++11) - Trailing return type, useful if the return type depends on argument names, such as template <class T, class U> auto add(T t, U u) -> decltype(t + u); or is complicated, such as in auto fpif(int)->int(*)(int)

Function declarators can be mixed with other declarators, where decl-specifier-seq allows:

// declares an int, an int*, a function, and a pointer to a function
int a = 1, *p = NULL, f(), (*pf)(double);
// decl-specifier-seq is int
// declarator f() declares (but doesn't define)
//                a function taking no arguments and returning int
struct S {
    virtual int f(char) const, g(int) &&; // declares two non-static member functions
    virtual int f(char), x; // compile-time error: virtual (in decl-specifier-seq)
                            // is only allowed in declarations of non-static
                            // member functions

If the decl-specifier-seq of the function declaration contains the keyword auto, trailing return type may be omitted, and will be deduced by the compiler from the type of the expression in the first return statement.

auto f() { return 42; } // return type is int
auto sum(int i) {
    if (i == 1)
        return i; // sum’s return type is int
        return sum(i-1)+i; // OK, sum’s return type is already known
(since C++14)

Parameter list

Parameter list determines the arguments that can be specified when the function is called. It is a comma-separated list of parameter declarations, each of which has the following syntax

attr(optional) decl-specifier-seq declarator (1)
attr(optional) decl-specifier-seq declarator = initializer (2)
attr(optional) decl-specifier-seq abstract-declarator(optional) (3)
attr(optional) decl-specifier-seq abstract-declarator(optional) = initializer (4)
... (5)
void (6)
1) Declares a named (formal) parameter. For the meanings of decl-specifier-seq and declarator, see declarations.
int f(int a, int *p, int (*(*x)(double))[3]);
2) Declares a named (formal) parameter with a default value.
int f(int a = 7, int *p = nullptr, int (*(*x)(double))[3] = nullptr);
3) Declares an unnamed parameter
int f(int, int *, int (*(*)(double))[3]);
4) Declares an unnamed parameter with a default value
int f(int = 7, int * = nullptr, int (*(*)(double))[3] = nullptr);
5) Declares a variadic function, may only appear as the last parameter in a parameter list.
int printf(const char* fmt, ...);
6) Indicates that the function takes no parameters, it is the exact synonym for an empty parameter list: int f(void); and int f(); declare the same function. Note that the type void cannot be used in a parameter list otherwise: int f(void, int); is an error (although derived types, such as void* can be used).

Parameter names are unused in function declarations, but are allowed for self-documenting purposes. They are used (and remain optional) in function definitions.

The type of each function parameter in the parameter list is determined according to the following rules:

1) First, decl-specifier-seq and the declarator are combined as in any declaration to determine the type.
2) If the type is "array of T" or "array of unknown bound of T", it is replaced by the type "pointer to T"
3) If the type is a function type F, it is replaced by the type "pointer to F"
4) Top-level cv-qualifiers are dropped from the parameter type

Because of these rules, the following function declarations declare exactly the same function:

int f(char s[3]);
int f(char[]);
int f(char* s);
int f(char* const);
int f(char* volatile s);

The following declarations also declare exactly the same function

int f(int());
int f(int (*g)());

If the last parameter before the ellipsis parameter in a variadic function is a function parameter pack, the comma between the ellipsis that ends the parameter pack and the ellipsis that indicates variadic argument is not required, and in fact, space is not required either, the following function templates are exactly the same:

template<typename ...Args> void f(Args..., ...);
template<typename ...Args> void f(Args... ...);
template<typename ...Args> void f(Args......);

An example of when such declaration is used is the implementation of std::is_function

Non-member function definitions

Example 1: non-member functions

#include <iostream>
#include <string>
// declaration in namespace(file) scope
// (the definition is provided later)
int f1();
// simple function with a default argument, returning nothing
void f0(const std::string& arg = "world") {
    std::cout << "Hello, " << arg << '\n';
// function returning a pointer to f0
auto fp11() -> void(*)(const std::string&) {
    return f0;
// function returning a pointer to f0, pre-C++11 style
void (*fp03())(const std::string&) {
    return f0;
int main()
    int f2(std::string); // declaration in function scope
    std::cout << f2("bad12") << '\n';
// simple non-member function returning int
int f1() {
    return 42;
// function with an exception specification and a function try block
int f2(std::string str) noexcept try { 
    return std::stoi(str);
} catch(const std::exception& e) {
    std::cerr << "stoi() failed!\n";
    return 0;


Hello, world
Hello, test
Hello, again
stoi() failed!