6.2.6. Functions with Varying Parameters

Sometimes we do not know in advance how many arguments we need to pass to a function. For example, we might want to write a routine to print error messages generated from our program. We’d like to use a single function to print these error messages in order to handle them in a uniform way. However, different calls to our error-printing function might pass different arguments, corresponding to different kinds of error messages.

The new standard provides two primary ways to write a function that takes a varying number of arguments: If all the arguments have the same type, we can pass a library type named initializer_list. If the argument types vary, we can write a special kind of function, known as a variadic template, which we’ll cover in § 16.4 (p. 699).

C++ also has a special parameter type, ellipsis, that can be used to pass a varying number of arguments. We’ll look briefly at ellipsis parameters in this section. However, it is worth noting that this facility ordinarily should be used only in programs that need to interface to C functions.

initializer_list Parameters
Image

We can write a function that takes an unknown number of arguments of a single type by using an initializer_list parameter. An initializer_list is a library type that represents an array (§ 3.5, p. 113) of values of the specified type. This type is defined in the initializer_list header. The operations that initializer_list provides are listed in Table 6.1.

Table 6.1. Operations on initializer_lists

Image

Like a vector, initializer_list is a template type (§ 3.3, p. 96). When we define an initializer_list, we must specify the type of the elements that the list will contain:

initializer_list<string> ls; // initializer_list of strings
initializer_list<int> li;    // initializer_list of ints

Unlike vector, the elements in an initializer_list are always const values; there is no way to change the value of an element in an initializer_list.

We can write our function to produce error messages from a varying number of arguments as follows:

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

The begin and end operations on initializer_list objects are analogous to the corresponding vector members (§ 3.4.1, p. 106). The begin() member gives us a pointer to the first element in the list, and end() is an off-the-end pointer one past the last element. Our function initializes beg to denote the first element and iterates through each element in the initializer_list. In the body of the loop we dereference beg in order to access the current element and print its value.

When we pass a sequence of values to an initializer_list parameter, we must enclose the sequence in curly braces:

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

Here we’re calling the same function, error_msg, passing three values in the first call and two values in the second.

A function with an initializer_list parameter can have other parameters as well. For example, our debugging system might have a class, named ErrCode, that represents various kinds of errors. We can revise our program to take an ErrCode in addition to an initializer_list as follows:

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

Because initializer_list has begin and end members, we can use a range for5.4.3, p. 187) to process the elements. This program, like our previous version, iterates an element at a time through the braced list of values passed to the il parameter.

To call this version, we need to revise our calls to pass an ErrCode argument:

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

Ellipsis Parameters
Image

Ellipsis parameters are in C++ to allow programs to interface to C code that uses a C library facility named varargs. Generally an ellipsis parameter should not be used for other purposes. Your C compiler documentation will describe how to use varargs.


Image Warning

Ellipsis parameters should be used only for types that are common to both C and C++. In particular, objects of most class types are not copied properly when passed to an ellipsis parameter.


An ellipsis parameter may appear only as the last element in a parameter list and may take either of two forms:

void foo(parm_list, ...);
void foo(...);

The first form specifies the type(s) for some of foo’s parameters. Arguments that correspond to the specified parameters are type checked as usual. No type checking is done for the arguments that correspond to the ellipsis parameter. In this first form, the comma following the parameter declarations is optional.


Exercises Section 6.2.6

Exercise 6.27: Write a function that takes an initializer_list<int> and produces the sum of the elements in the list.

Exercise 6.28: In the second version of error_msg that has an ErrCode parameter, what is the type of elem in the for loop?

Exercise 6.29: When you use an initializer_list in a range for would you ever use a reference as the loop control variable? If so, why? If not, why not?


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.255.140