标准库类initializer_list
新标准下,我们在编写一个接受单个类型的未知数量的参数的函数时,可以使用initializer_list
参数作为函数的参数。initializer_list
是一种标准库类型,它表示一个由指定类型的值组成的数组。此类型在initializer_list
头文件中定义。 下表列出了initializer_list
类提供的操作。
操作 | 功能 |
---|---|
initializer_list |
默认初始化; 创建一个元素类型为T的空列表。 |
initializer_list |
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
的数组的指针。我们必须记住,decltype
。decltype
返回的类型是一个数组类型,所以我们必须添加一个*
来指示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
函数可能返回常量表达式,也可能返回非常量表达式,编译器未对此作出要求。
通常将
inline
和constexpr
函数定义在头文件中。
参考文献
[1] Lippman S B , Josée Lajoie, Moo B E . C++ Primer (5th Edition)[J]. 2013.