Declaring functions
A function declaration introduces the function name and its type. A function definition associates the function name/type with the function body.
Contents |
[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)
|
(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)
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 };
[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) | ||||||||
int f(int a, int *p, int (*(*x)(double))[3]);
int f(int a = 7, int *p = nullptr, int (*(*x)(double))[3] = nullptr);
int f(int, int *, int (*(*)(double))[3]);
int f(int = 7, int * = nullptr, int (*(*)(double))[3] = nullptr);
int printf(const char* fmt, ...);
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:
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)());
[edit] Non-member function definitions
This section is incomplete |
[edit] Example 1: non-member functions
This section is incomplete Reason: move/cleanup |
#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] Example 2: member functions
This section is incomplete Reason: move to member_functions |
#include <iostream> #include <string> #include <utility> #include <exception> struct S { int data; // simple converting constructor (declaration) S(int val); // simple explicit constructor (declaration) explicit S(std::string str); // const member function (definition) virtual int getData() const { return data; } }; // definition of the constructor S::S(int val) : data(val) { std::cout << "ctor1 called, data = " << data << '\n'; } // this constructor has a catch clause S::S(std::string str) try : data(std::stoi(str)) { std::cout << "ctor2 called, data = " << data << '\n'; } catch(const std::exception&) { std::cout << "ctor2 failed, string was '" << str << "'\n"; throw; // ctor's catch clause should always rethrow } struct D : S { int data2; // constructor with a default argument D(int v1, int v2 = 11) : S(v1), data2(v2) {} // virtual member function int getData() const override { return data*data2; } // lvalue-only assignment operator D& operator=(D other) & { std::swap(other.data, data); std::swap(other.data2, data2); return *this; } }; int main() { D d1 = 1; S s2("2"); try { S s3("not a number"); } catch(const std::exception&) {} std::cout << s2.getData() << '\n'; D d2(3, 4); d2 = d1; // OK: assignment to lvalue // D(5) = d1; // ERROR: no suitable overload of operator= }
Output:
ctor1 called, data = 1 ctor2 called, data = 2 ctor2 failed, string was 'not a number' 2 ctor1 called, data = 3