Overview

C++的函数声明介绍了函数的名称和函数的类型,而函数的定义是将函数的声明和函数体关联起来。

Function Declaration

函数声明能够出现在任何Scope内。如果函数声明出现在class scope内(除了使用friend specifier),则该函数称为member functions,函数类型是由return type(符合declaration syntaxdecl-specifier-seq)和function declarator组成的。


noptr-declaratorhello ( parameter-list ) cv(optional) ref(optional) except(optional) attr(optional) (1)
noptr-declarator ( parameter-list ) cv(optional) ref(optional) attr(optional) ->trailing (2)

(1) 普通的函数声明语法

(2) 尾部返回类型声明:尾部返回类型只允许在函数声明的最外层,这种情况下decl-specifier-seq只能是关键字 auto

noptr-declarator - 任何合理的declarator,但是以*,&或者&&为开头的必须用括号包围
parameter-list - 可能为空,以逗号分隔的函数参数列表
attr(C++11) - 可选的attributes列表,这些属性应用于函数类型,而不是函数本身。函数的属性出现在函数的声明符中的标识符之后,并且和出现在声明开头的属性结合(如果有的话)。
cv - const/volatile限定,只允许出现在no-static member function声明中
ref(C++11) - ref-qualification(引用限定),只允许出现在no-static member function声明中
except - dynamic exception specification(Until C++2017)或者noexcept specification(C++11).注意exception specification(异常规范)并不是函数类型的一部分(Until C++17)
trailing (C++11) - Trailing return type(尾置返回类型),如果返回值依赖于参数名字,将会非常有用,比如
1
template<typename T, typename U> auto add(T t, U u) ->decltype(t + u);
,更复杂的,比如:
1
auto fpif(int) -> int(*)int

// 声明一个 int, int*, 一个函数,一个指向函数的指针
int i, *p = NULL, f(), (*pf)(double);
// decl-specifier-seq 是int
// 声明符f声明(并非定义)了一个返回值为int,参数列表为空的函数

struct S {
    virtual int f(char) const, g(int) &&; // 定义了两个 non-static member function

    virtual int f(char), x; // 编译期错误:virtual(in decl-specifier-seq)只允许出现在
                            // non-static member function的声明中
};

使用volatile-qualified object 作为参数类型或者返回类型已经被废弃(Since C++20)

函数的返回类型不能是函数类型或者数组类型(但是可以是它们的指针或者引用) 和任何declaration(声明)一样,出现在声明之前的属性和随即出现在声明符的标识符之后的属性同时应用于被声明或被定义的实体(比如这种情况下,对于函数)
1
2

[[noreturn]] void f [[noreturn]] (); // 两个都应用于 function f
(Since C++11) 但是,出现在declarator(声明符)之后的属性应用于函数类型而不是函数本身。
1
void f() [[noreturn]]; // 错误:该属性对于类型无效
对于任何declaration(声明),被声明为 ret func(params) 的函数func的类型为 ret(params) (除了下述说明的参数类型重写外):更多细节看type naming

Return type deduction

如果 decl-specifier-seq 包含了 auto 关键字,尾部返回类型可能会被忽略,返回类型将会被编译器推断通过返回语句的表达式,如果返回类型并未使用 decltype(auto) ,则推断将会符合template argument deduction

int x = 1;
auto f() { return x; } // return type is int
const auto& f() { return x; } // return type is const int&

如果返回类型是decltype(auto),返回类型应与包装在decltype的表达式返回的类型相同.

int x = 1;
decltype(auto) f() { return x; } // return type is int, is same as decltype(x)
decltype(auto) f() { return (x); } // return type is int&, is same as decltype((x))

注意: const decltype(auto)& 是错误的, decltype(auto) 必须单独使用.
如果有多条返回语句,它们必须推导成相同的类型

auto f(bool val)
{
    if (val) return 123; // deduces return type int
    else return 3.14f; // error: deduces return type float
}

如果没有返回语句或者返回语句返回 void expression ,声明的返回类型必须为 decltype(auto) ,在这种情况下,则推断返回类型为 void ,或者(可能 cv-qulified ) auto ,在这种情况下,则推断返回类型为 void (相同的 cv-qulified ) void

auto f() { } // returns void
auto g() { return f(); } // returns void
auto* x() { } // error: can't deduce auto* from void

一旦函数的返回语句出现,那么返回类型推断能够用于该函数剩余的部分,包括在其他返回语句当中.

auto sum(int i)
{
    if (i == 1)
    {
        return i; // sum's return type is int
    }
    else
    {
        return sum(i - 1) + i; // okay: sum's return type is already known
    }
}

如果返回语句包含 brace-init-list ,推断是不被允许的.

auto func() { return {1, 2, 3}; } // error

Virtual functionscoroutines(Since C++20)

struct F
{
    virtual auto f() { return 2; } // error
}

如果一个函数使用return type deduction,那么该函数不能使用推断的类型重新声明或者另一种类型return type deduction的声明即使它推断的是相同的类型.

auto f(); // declared, not yet defined
auto f() { return 42; } // defined, return type is int
int f(); // error: can't use deduced type
decltype(auto) f(); // error: different kind of deduction
auto f(); // okay: re-declared

template<typename T>
struct A { friend T frf(T); }
auto frf(int i) { return i; } // not a friend of A<int>

除了用户自定义的转换函数外的{< better-anchor href=“/” hint=“Function templates”>}}都能使用return type deduction.推断发生在实例化,即使在返回语句中的表达式是独立的,该实例化不是即时的上下文对于SFINAE的期望.

template<typenmae T> auto f(T t) { return t; }
typedef decltype(f(1)) fint_t; // instantiates f<int> to deduce return type
template<typename T> auto f(T* t) { return *t; }
void g() { int (*p)(int*) = &f; } // instantiates both fs to determine return types.
                                  // choose second template overload

特例化的函数模板使用return type deduction必须使用相同的返回类型占位符

template<typename T> auto g(T t) { return t; } // #1
template auto g(int); // okay: return type is int.
// template char g(char); error: no matching template

template<> auto g(double); // okay: forward declaration with unknown return type
template<typename T> T g(T t) { return t; } // okay: is not equivalent to #1
template char g(char); // okay: now is matching template
template auto g(float); // still matchs #1
// void h() { return g(42); } // error: ambiguous

显式化声明本身不会实例化使用return type deduction的函数模板.

template<typename T> auto f(T t) { return t; }
extern template auto f(int t); // does not instantiates 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