Declaring functions - cppreference.com

##### cppreference.com

##### Search

Create account

##### Namespaces

- Page
- Discussion

##### Variants

##### Views

- View
- Edit
- History

##### Actions

# Declaring functions

From cppreference.com
< cpp‎ | language

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

### [edit] 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) requires(concepts TS) | (1) |
noptr-declarator `(` parameter-list `)` cv(optional) ref(optional) except(optional) attr(optional) `->` trailing requires(concepts TS) | (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. These attributes are applied to the type of the function, not the function itself. The attributes for the function appear after the identifier within the declarator and are combined with the attributes that appear in the beginning of the declaration, if any.
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(until C++17) or noexcept specification(C++11) Note that the exception specification is not part of the function type (until C++17)
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)
requires(concepts TS) | - | The requires clause, which declares the associated constraints for the function, which must be satisfied in order for the function to be selected by overload resolution. Note that the associated constraint is part of function signature, but not part of function type.

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

The return type of a function cannot be a function type or an array type (but can be a pointer or reference to those).

As with any declaration, attributes that appear before the declaration and the attributes that appear immediately after the identifier within the declarator both apply to the entity being declared or defined (in this case, to the function)
    [[noreturn]] void f [[noreturn]] (); // OK, both attributes apply to the function f
However, the attributes that appear after the declarator (in the syntax above), apply to the type of the function, not to the function itself
    void f() [[noreturn]]; // Error: this attribute has no effect on a type
| (since C++11)

### Return type deduction 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 used in the return statement. If the return type does not use `decltype(auto)`, the deduction follows the rules of template argument deduction.
    int x = 1;
    auto f() { return x; } // return type is int
    const auto& f() { return x; } // return type is const int&
If the return type is `decltype(auto)`, the return type is as what would be obtained if the expression used in the return statement were wrapped in decltype.
    int x = 1;
    decltype(auto) f() { return x; } // return type is int, same as decltype(x)
    decltype(auto) f() { return(x); } // return type is int&, same as decltype((x))
(note: "`const decltype(auto)&`" is an error, `decltype(auto)` must be used on its own) If there are multiple return statements, they must all deduce to the same type
    auto f(bool val)
    {
        if(val) return 123; // deduces return type int
        else    return 3.14f; // deduces return type float: error
    }
If there is no return statement or if the argument of the return statement is a void expression, the declared return type must be either decltype(auto), in which case the deduced return type is void, or (possibly cv-qualified) auto, in which case the deduced return type is then (identically cv-qualified) void.
    auto f() {} // returns void
    auto g() { return f(); } // returns void
    auto* x() {} // error: cannot deduce auto* from void
Once a return statement has been seen in a function, the return type deduced from that statement can be used in the rest of the function, including in other return statements.
    auto sum(int i)
    {
        if(i == 1)
            return i; // sum’s return type is int
        else
            return sum(i - 1) + i; // OK, sum’s return type is already known
    }
If the return statement uses a brace-init-list, deduction is not allowed:
    auto func () { return {1, 2, 3}; }  // error
Virtual functions cannot use return type deduction.
    struct F
    {
        virtual auto f() { return 2; } // error
    };
If a function uses return type deduction, it cannot be redeclared using the type that it deduces to, or another kind of return type deduction even if it deduces to the same type
    auto f();               // declared, not yet defined
    auto f() { return 42; } // defined, return type is int
    int f();                // error, cannot use the deduced type
    decltype(auto) f();     // error, different kind of deduction
    auto f();               // re-declared, OK

    template<typename T>
    struct A { friend T frf(T); };
    auto frf(int i) { return i; } // not a friend of A<int>
Function templates other than user-defined conversion functions can use return type deduction. The deduction takes place at instantiation even if the expression in the return statement is not dependent. This instantiation is not in an immediate context for the purposes of SFINAE.
    template<class T> auto f(T t) { return t; }
    typedef decltype(f(1)) fint_t; // instantiates f<int> to deduce return type
    template<class T> auto f(T* t) { return *t; }
    void g() { int (*p)(int*) = &f; } // instantiates both fs to determine return types,
                                      // chooses second template overload
Specializations of function templates that use return type deduction must use the same return type placeholders
    template<typename T> auto g(T t) { return t; } // #1
    template auto g(int);      // OK, return type is int
    //template char g(char);   // error, no matching template

    template<> auto g(double); // OK, forward declaration with unknown return type
    template<typename T> T g(T t) { return t; } // OK, not equivalent to #1
    template char g(char);     // OK, now there is a matching template
    template auto g(float);    // still matches #1
    // void h() { return g(42); } // error, ambiguous
Explicit instantiation declarations do not themselves instantiate function templates that use return type deduction
    template<typename T> auto f(T t) { return t; }
    extern template auto f(int); // does not instantiate f<int>
    int (*p)(int) = f; // instantiates f<int> to determine its return type,
                       // but an explicit instantiation definition
                       // is still required somewhere in the program
| (since C++14)

### [edit] 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` (possibly cv-qualified) cannot be used in a parameter list otherwise: int f(void, int); and int f(const void); are errors (although derived types, such as `void*` can be used). In a template, only non-dependent void type can be used (a function taking a single parameter of type `T` does not become a no-parameter function if instantiated with `T = void`) (since C++11)

Although decl-specifier-seq implies there can exist specifiers other than type specifiers, the only other specifier allowed is register as well as auto (until C++11), and it has virtually no effect.

If any of the function parameters uses a placeholder (either `auto` or a constrained type), the function declaration is instead an abbreviated function template declaration:
    void f(auto (auto::*)(auto)); // #1
    template<typename T, typename U, typename V> void f(T (U::*)(V)); // same as #1

    void g1(const C1*, C2&); // #2 (assuming C1 and C2 are concepts
    template<C1 T, C2 U> void g1(const T*, U&); // same as #2
| (concepts TS)

Parameter names declared in function declarations are usually for only self-documenting purposes. They are used (but 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 (This adjustment only affects the function type, but doesn't modify the property of the parameter: int f(const int p, decltype(p)*); and int f(int, const int*); declare the same function)

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)());

Parameter type cannot be a type that includes a reference or a pointer to array of unknown bound, including a multi-level pointers/arrays of such types, or a pointer to functions whose parameters are such types | (until C++14)

The comma before the ellipsis parameter that indicates variadic arguments is optional, even if it follows the ellipsis that indicates a parameter pack expansion, so 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

### [edit] Function definition

A non-member function definition may appear at namespace scope only (there are no nested functions). A member function definition may also appear in the body of a class definition. They have the following syntax:

attr(optional) decl-specifier-seq(optional) declarator virt-specifier-seq(optional) function-body |  |

where function-body is one of the following

ctor-initializer(optional) compound-statement | (1) |
function-try-block | (2) |
`=` `delete` `;` | (3) | (since C++11)
`=` `default` `;` | (4) | (since C++11)

1) regular function body
2) function-try-block (which is a regular function body wrapped in a try/catch block)
3) explicitly deleted function definition
4) explicitly defaulted function definition, only allowed for special member functions

attr(C++11) | - | optional list of attributes. These attributes are combined with the attributes after the identifier in the declarator (see top of this page), if any.
decl-specifier-seq | - | the return type with specifiers, as in the declaration grammar
declarator | - | function declarator, same as in the function declaration grammar above
virt-specifier-seq(C++11) | - | override, final, or their combination in any order (only allowed for member functions)
ctor-initializer | - | member initializer list, only allowed in constructors
compound-statement | - | the brace-enclosed sequence of statements that constututes the body of a function

    int max(int a, int b, int c)
    {
        int m = (a > b)? a : b;
        return (m > c)? m : c;
    }
    // decl-specifier-seq is "int"
    // declarator is "max(int a, int b, int c)"
    // body is { ... }

The function body is a compound statement (sequence of zero or more statements surrounded by a pair of curly braces), which is executed when the function call is made.

The parameter types, as well as the return type of a function cannot be incomplete class types, except for deleted functions (since C++11). The completeness check is made in the context of the function body, which allows member functions to return the class in which they are defined (or its enclosing class), even if it's incomplete at the point of definition (it is complete in the function body).

The parameters declared in the declarator of a function definition are in scope within the body. If a parameter is not used in the function body, it does not need to be named (it's sufficient to use an abstract declarator)

    void print(int a, int) // second parameter is not used
    {
        std::printf("a = %d\n",a);
    }

Even though top-level cv-qualifiers on the parameters are discarded in function declarations, they modify the type of the parameter as visible in the body of a function:

    void f(const int n) // declares function of type void(int)
    {
        // but in the body, the type of n is const int
    }

### Deleted functions If, instead of a function body, the special syntax `= delete ;` is used, the function is defined as deleted. Any use of a deleted function is ill-formed (the program will not compile). This includes calls, both explicit (with a function call operator) and implicit (a call to deleted overloaded operator, special member function, allocation function etc), constructing a pointer or pointer-to-member to a deleted function, and even the use of a deleted function in an unevaluated expression. However, implicit ODR-use of a non-pure virtual member function that happens to be deleted is allowed. If the function is overloaded, overload resolution takes place first, and the program is only ill-formed if the deleted function was selected.
    struct sometype
    {
        void* operator new(std::size_t) = delete;
        void* operator new[](std::size_t) = delete;
    };
    sometype* p = new sometype; // error, attempts to call deleted sometype::operator new
The deleted definition of a function must be the first declaration in a translation unit: a previously-declared function cannot be redeclared as deleted:
    struct sometype { sometype(); };
    sometype::sometype() = delete; // error: must be deleted on the first declaration
### __func__ Within the function body, the function-local predefined variable __func__ is defined as if by
    static const char __func__[] = "function-name";
This variable has block scope and static storage duration:
    struct S
    {
        S(): s(__func__) {} // OK: initializer-list is part of function body
        const char* s;
    };
    void f(const char* s = __func__); // error: parameter-list is part of declarator
| (since C++11)

### [edit] Example 1: non-member functions

| This section is incomplete Reason: move/cleanup

Run this code

    #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()
    {
        f0();
        fp11()("test");
        fp03()("again");
        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;
    }

Output:

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

### [edit] Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR | Applied to | Behavior as published | Correct behavior
--- | --- | --- | ---
CWG 1394 | c++11 | deleted function could not return an incomplete type | incomplete return type allowed
CWG 577 | c++11 | dependent type void could be used to declare a no-parameter function | only non-dependent void is allowed
CWG 393 | c++14 | types that include pointers/references to array of unknown bound can't be parameters | such types are allowed

Retrieved from "http://en.cppreference.com/mwiki/index.php?title=cpp/language/function&oldid=91997"
Category:

- Todo with reason

Hidden category:

- Pages with unreviewed CWG DR marker