C++11 uses variable parameters in function templates and class templates

Posted by malcolmboston on Sat, 05 Mar 2022 07:55:00 +0100

reference

1,http://c.biancheng.net/view/vip_8692.html

The so-called variable parameter means that the number and type of parameters can be arbitrary.

For function parameters, C + + always supports setting variable parameters for functions. The most typical representative is the printf() function. Its syntax format is:

int printf ( const char * format, ... );

... means variable parameters, that is, the printf() function can receive any number of parameters, and the types of each parameter can be different, for example:

printf("%d", 10);
printf("%d %c",10, 'A');
printf("%d %c %f",10, 'A', 1.23);

We usually call variable parameters that contain multiple parameters as parameter packages. With the help of format string, the printf() function can easily determine the number and type of parameters in the parameter package.

In the following program, a simple variable parameter function is customized:

#include <iostream>
#include <cstdarg>
//Variable parameter function
void vair_fun(int count, ...)
{
    va_list args;
    va_start(args, count);
    for (int i = 0; i < count; ++i)
    {
        int arg = va_arg(args, int);
        std::cout << arg << " ";
    }
    va_end(args);
}
int main()
{
    //There are 4 variable parameters, namely 10, 20, 30 and 40
    vair_fun(4, 10, 20, 30,40);
    return 0;
}

Vair in program_ The fun() function has two parameters, one is count, and the other is... Variable parameters. We can easily use the count parameter inside the function, but to use the parameters in the parameter package, we need to use the VA in the header file_ start,va_arg and va_end these three macros with parameters:

va_start(args, count): args is va_list variable, we can simply regard it as char * type. With the help of the count parameter, find the starting position of the variable parameter and assign it to args;

va_arg(args, int): call va_start on the premise of finding the starting position of the variable parameter, by indicating that the parameter type is int, va_arg can return the first parameter in the variable parameters;

va_end(args): call VA in time after args variable is no longer used_ The end macro cleans the args variable.

Note that with the help of va_arg when getting the parameters in the parameter package, va_arg does not have the ability of self termination, so the program uses the count parameter to control va_arg, and then read out all parameters. Control VA_ There are other methods for Arg execution times, such as terminating when the specified data is read.

In the process of using... Variable parameters, pay attention to the following points:
1. ... the variable parameter must be the last parameter of the function, and a function can only have 1 variable parameter at most.
2. The variable parameter must be preceded by at least one famous parameter (for example, the count parameter in the above example);
3. When the variable parameter contains a parameter of type char, va_arg macro should be read in the form of int; When a variable parameter contains a parameter of type short, Va_ The Arg macro is to be read in the form of double.

It should be noted that the method of... Variable parameters is only applicable to function parameters, not template parameters. C++11 standard provides a method to realize variable template parameters.

Variable parameter template

Before the release of C++11 standard, function templates and class templates can only set a fixed number of template parameters. The C++11 standard extends the function of the template and allows the template to contain any number of template parameters. Such a template is also called variable parameter template.

1) Variable parameter function template

The function template is explained first. A function template with variable parameters is defined as follows:

template<typename... T>
void vair_fun(T...args) {
    //Function body
}

In template parameters, typename (or class) followed by... Indicates that T is a variable template parameter, which can receive multiple data types, also known as template parameter package. vair_ In the fun() function, the type of args parameter is represented by T... which means that args parameter can receive any parameter, also known as function parameter packet.

This means that the vair finally instantiated from this function template_ The fun() function can specify any type and any number of parameters. For example, we can use this function template as follows:

vair_fun();
vair_fun(1, "abc");
vair_fun(1, "abc", 1.23);

The difficulty of using variable parameter template is how to "unlock" the parameter package (using the data in the package) within the template function. Here are two simple methods.

[unpacking recursively]

Let's take a look at an example:

#include <iostream>
using namespace std;

//Recursive exit of template function
void vir_fun() {
}

template <typename T, typename... args>
void vir_fun(T argc, args... argv)
{
    cout << argc << endl;
    //Start recursion and retransmit the argv parameter package outside the first parameter to vir_fun
    vir_fun(argv...);
}

int main()
{
    vir_fun(1, "http://www.biancheng.net", 2.34);
    return 0;
}

The implementation results are:

1
http://www.biancheng.net
2.34

Analyze the execution process of a program:
First, the main() function calls vir_ When using the fun() template function, it can be easily determined that the type of the template parameter T is int, the value of the function parameter argc is 1, and the remaining template parameters and function parameters are located in args and argv respectively according to the value of the passed argument;

vir_ In the fun() function, first output the value of argc (1), then call itself repeatedly, and pass the data in the function parameter package argv to the formal parameters argc and argv as arguments;

Execute vir again_ Fun() function. At this time, the type of template parameter T is char *, and the value of output argc is "http:www.biancheng.net". Call itself again and continue to take the data in the argv package as the argument;

Execute vir again_ Fun() function. At this time, the type of template parameter T is double and the value of output argc is 2.34. Call itself again and take the empty argv package as the argument;

Since there is no data in argv package, vir without any formal parameters and empty function body will be called at this time_ Fun() function, and finally the execution ends.

To unpack recursively, be sure to set the exit at the end of recursion. For example, in this example, vir with an invisible parameter and an empty function body_ The fun () function is the exit to the end of recursion.

[unpacking by non recursive method]

With the help of comma expression and initialization list, you can also unpack the parameter package.

Vir_ Take fun() function as an example. The following program demonstrates the unpacking process of non recursive method:

#include <iostream>
using namespace std;

template <typename T>
void dispaly(T t) {
    cout << t << endl;
}

template <typename... args>
void vir_fun(args... argv)
{
    //Comma expression + initialization list
    int arr[] = { (dispaly(argv),0)... };
}

int main()
{
    vir_fun(1, "http://www.biancheng.net", 2.34);
    return 0;
}

Here, we focus on the code in line 13. We initialize the array arr in the way of {} initialization list, (display(argv),0)... Will be expanded into (display(1),0) and (display) in turn(“ http://www.biancheng.net ”), 0) and (display(2.34),0).

In other words, the code in line 13 is equivalent to the following code:

 int arr[] = { (dispaly(1),0), (dispaly("http://www.biancheng.net"),0), (dispaly(2.34),0) };

You can see that each element is a comma expression. Take (display(1), 0) as an example. It will first calculate display(1), and then return 0 as the value of the whole expression to the array. Therefore, the arr array finally stores 0. The arr array is purely to expand the parameter package and does not play any other role.

2) Variable parameter class template

In the C++11 standard, the template parameter in the class template can also be a variable parameter. The type tuple class provided by C++11 standard is a typical variable parameter template class. Its definition is as follows:

template <typename... Types>
class tuple;

Unlike the class with fixed template parameters, the type template class can receive any number and type of template parameters when instantiated, for example:

std:tuple<> tp0;
std::tuple<int> tp1 = std::make_tuple(1);
std::tuple<int, double> tp2 = std::make_tuple(1, 2.34);
std::tuple<int, double, string> tp3 = std::make_tuple(1, 2.34, "http://www.biancheng.net");

The following code shows a class template that supports variable parameters:

#include <iostream>
//Declaration template class demo
template<typename... Values> class demo;
//Exit of inherited recursion
template<> class demo<> {};
//Unpack by inheritance
template<typename Head, typename... Tail>
class demo<Head, Tail...>
    : private demo<Tail...>
{
public:
    demo(Head v, Tail... vtail) : m_head(v), demo<Tail...>(vtail...) {
        dis_head();
    }
    void dis_head() { std::cout << m_head << std::endl; }
protected:
    Head m_head;
};
int main() {
    demo<int, float, std::string> t(1, 2.34, "http://www.biancheng.net");
    return 0;
}

In the program, the Tail in the demo template parameter is a parameter package, and the unpacking method is implemented in the way of "recursion + inheritance". Specifically, when the demo < head, Tail... > class is instantiated, because it inherits from the demo < Tail... > class, the parent class will also be instantiated and recurse until the Tail parameter package is empty. At this time, the demo template class with empty template parameter list will be called.

The output of the program is:

http://www.biancheng.net
2.34
1

Topics: C++ Back-end