C++11——函数

标准库类initializer_list

新标准下,我们在编写一个接受单个类型的未知数量的参数的函数时,可以使用initializer_list参数作为函数的参数。initializer_list是一种标准库类型,它表示一个由指定类型的值组成的数组。此类型在initializer_list头文件中定义。 下表列出了initializer_list类提供的操作。

操作 功能
initializer_list lst; 默认初始化; 创建一个元素类型为T的空列表。
initializer_list lst{a,b,c...}; lst具有与初始化器一样多的元素;且元素是初始化器中元素的副本。 初始化器中的元素是const。
lst2(lst)
lst2 = lst
拷贝或分配一个initializer_list但不会赋值列表中的元素。拷贝后,原副本和拷贝副本共享列表中的元素。
lst.size() 返回列表中元素的个数。
lst.begin()
lst.end()
返回一个指向lst中第一个元素和最后一个元素后一位置的指针。

initializer_list是一个模版类型,初始化时需要指明所包含的元素的类型:

Code:
    initializer_list ls;   // initializer_list of strings
    initializer_list li;      // initializer_list of ints

initializer_list中元素是const的,所以不能改变initializer_list中元素的值。
我们可以编写函数来从不同数量的参数中生成错误消息,如下所示:

Code:
    void error_msg(initializer_list il)
    {
        for (auto beg = il.begin(); beg != il.end(); ++beg)
            cout << *beg << " " ;
        cout << endl;
    }

我们的函数初始化beg表示initializer_list中第一个元素并遍历initializer_list中的每个元素。 在循环体中,我们解引用以访问当前元素并打印其值。
当我们将一系列值传递给initializer_list参数时,我们必须将值序列括在花括号中:

Code:
    // expected, actual are strings
    if (expected != actual)
        error_msg({"functionX", expected, actual});
    else
        error_msg({"functionX", "okay"});

这里我们调用相同的函数error_msg,在第一次调用中传递三个值,在第二个调用中传递两个值。
具有initializer_list参数的函数也可以具有其他参数。 例如,我们的调试系统时可能有一个名为ErrCode的类,它表示各种错误 除了initializer_list之外,我们可以修改我们的程序以获取ErrCode,如下所示:

Code:
    void error_msg(ErrCode e, initializer_list il)
    {
        cout << e.msg() << ": ";
        for (const auto &elem : il)
            cout << elem << " " ;
        cout << endl;
    }

要调用此版本函数,我们需要修改我们的调用方式以传递ErrCode参数:

Code:
    if (expected != actual)
        error_msg(ErrCode(42), {"functionX", expected, actual});
    else
        error_msg(ErrCode(0), {"functionX", "okay"});

以列表的方式初始化返回值

在新标准下,函数可以返回一个带括号的值列表。与任何其他返回一样,该列表用于初始化表示函数返回的临时值。如果该列表为空,则返回的临时值被构造函数进行默认初始化。如果未重载构造函数,则函数的返回值依赖于函数的返回类型。
与上文error_msg函数做对比,下面我们将重新编写一个函数,该函数返回一个vector,该vector用于存储含有错误信息的字符串:

Code:
    vector process()
    {
        // . . .
        // expected and actual are strings
        if (expected.empty())
            return {};                     // return an empty vector
        else if (expected == actual)
            return {"functionX", "okay"};  // return list-initialized vector
        else
            return {"functionX", expected, actual};
    }

在返回内置类型的函数中,被花括号括起来的列表最多可包含一个值,该值不能进行缩小转换。如果函数返回类类型,则类本身定义的构造函数知道如何使用初始化器。

使用尾随返回类型(Trailing Return Type)

在新标准下,一种简化函数声明的方法是使用尾随返回类型。可以为任何函数定义尾随返回,尤其对于具有复杂返回类型的函数(例如指向数组的指针(或引用))最有用。尾随返回类型在函数的参数列表后面,并以->开头。 为了表明返回类型在参数列表的后面,我们在函数声明中“返回类型”位置处使用auto

Code:
    // fcn takes an int argument and returns a pointer to an array of ten ints
    auto func(int i) -> int(*)[10];

因为返回类型位于参数列表之后,所以更容易看到func返回一个指针并且该指针指向含有10个int的数组。

使用decltype简化函数返回类型的声明

作为另一种选择,如果我们知道函数返回的指针指向的数组类型,我们可以使用decltype来声明返回类型。例如,以下函数返回指向两个数组的指针,具体取决于其参数的值:

Code:
    int odd[] = {1,3,5,7,9};
    int even[] = {0,2,4,6,8};
    // returns a pointer to an array of five int elements
    decltype(odd) *arrPtr(int i)
    {
        return (i % 2) ? &odd : &even;   // returns a pointer to the array
    }

使用decltype来表示arrPtr函数返回指向odd所具有的任何类型的指针。该odd是一个数组,因此arrPtr返回一个指向含有五个int的数组的指针。我们必须记住,decltypedecltype返回的类型是一个数组类型,所以我们必须添加一个*来指示arrPtr返回一个指针。

constexpr函数

constexpr函数是可以用在常量表达式中的函数。和其他函数一样constexpr函数的定义也需要满足一定的限制条件:该函数的返回类型和参数类型必须是文字类型(literal type),并且函数体中可以拥有的唯一可执行语句是return语句:

Code:
    constexpr int new_sz() { return 42; }
    constexpr int foo = new_sz();  // ok: foo is a constant expression

在这里,我们将new_sz定义为不带参数的constexpr函数。 编译器可以在编译时验证对new_sz的调用是否返回常量表达式,因此我们可以使用new_sz来初始化constexpr变量foo
当它这样使用constexpr函数时,编译器将用其返回的结果值替换对constexpr函数的调用。为了能够立即扩展函数,constexpr函数是隐式内联的。
constexpr函数体可以包含其他语句,只要这些语句在运行时不生成任何操作就可以。例如,constexpr函数可能包含空语句,类型别名和使用声明。
constexpr函数允许返回不是常量的值:

Code:
    // scale(arg) is a constant expression if arg is a constant expression
    constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }

如果传给scale的参数是常量表达式,那么该函数将返回一个常量表达式。否则该函数不会返回一个常量表达式:

Code:
    int arr[scale(2)];   // ok: scale(2) is a constant expression
    int i = 2;           // i is not a constant expression
    int a2[scale(i)];    // error: scale(i) is not a constant expression

此处我们传给scale的参数是一个常量表达式(文字值2),所以该调用将返回一个常量表达式。在这种情况下,编译器会把对函数scale的调用替换为该函数所返回的常量表达式。
如果我们传给scale的参数不是一个常量表达式(如上面代码传给scale的是一个int型变量i),那么该调用返回的就不是一个常量表达式。如果我们在需要常量表达式的上下文中使用scale,则编译器会检查scale返回结果是否为常量表达式。 如果不是,编译器将产生错误消息。
注:constexpr函数可能返回常量表达式,也可能返回非常量表达式,编译器未对此作出要求。

通常将inlineconstexpr函数定义在头文件中。

参考文献

[1] Lippman S B , Josée Lajoie, Moo B E . C++ Primer (5th Edition)[J]. 2013.

你可能感兴趣的:(C++11——函数)