[C ++ 11] C ++ template variable parameters

The variable parameter template

Original link: http://blog.csdn.net/xiaohu2022/article/details/69076281
common template can only take a fixed number of template parameters. Sometimes, however, we can expect to receive any number of template template parameters, this time variable parameter template can be used. For variable parameter template, which comprises at least one packet parameter template, the template parameters received packet is 0 or template of a plurality of parameters. Accordingly, the presence function parameter packet, means that this function may receive any number of parameters of the parameters.

Use Rules

A variable parameter template class is defined as follows:

template<typename ... Types>
class Tuple
{};

 

It can be any number of instances of the type Tuple:

Tuple<> t0;
Tuple<int> t1;
Tuple<int, string> t2;
// Tuple<0> error;  0 is not a type

 

If you want to avoid using 0 to instantiate the template parameter variable parameter template appears, you can be defined template:

template<typename T, typename ... Types>
class Tuple
{};

 

At this point in time instantiation, you must pass at least one template parameter , otherwise it is impossible to compile.
Similarly, define the variable parameters of the function may receive any parameter template:

Template <typename ... Types>
 void f (Types ... args); 

// legitimate calls 
f (); 
f ( 1 ); 
f ( 3.4 , " the Hello " );

 

For a class template, the template parameter variable package must be the last parameter template parameter list . But for the function template, it is not the limit, consider the following scenario:

Template <... Ts of typename, typename U>
 class Invalid 
{};    // This definition is illegal, because the type of never inferred U 

Template <... Ts of typename, typename U>
 void Valid (U U, Ts of args ...);   // this is legal, because it can be inferred type U
 // void invalid (Ts ... args, U U); // illegal, can never be inferred U 

! Valid ( 1.0 , 1 , 2 , 3 ); // At this time, U type is double, Ts is {int, int, int}

 

The variable parameter function template instantiations

Not directly traverse different parameters passed to the variable parameters of the template, but can make use of a recursive way to use a variable parameter template. The variable allows you to create type-safe variable length parameter list parameter template. The following definition of a variable parameter function template processValues ​​(), which allows type-safe way to accept a variable number of different types of parameters. The processValues ​​function () processes each value of the variable parameter list, the corresponding version of handleValue performed for each parameter ().

// processing of the actual function of each type of 
void handleValue ( int value) {COUT << " Integer: " << value << endl;}
 void handleValue ( Double value) {COUT << " Double: " << << value endl;}
 void handleValue ( String value) {COUT << " String: " << value << endl;} 

// for terminating the iteration function group 
Template <typename T> void the processValues (T Arg) 
{ 
    handleValue (Arg) ;
} // variable parameter function template



Template <typename T, ... Ts of typename> void the processValues (T Arg, ... Ts of args) 
{ 
    handleValue (Arg); 
    the processValues (args ...); // unpacked, and then recursively 
}

 

This example can be seen with the three ... operators, but there are two different meanings. In the parameter list of templates and function parameter list, which shows parameters package. As mentioned earlier, the parameter packet can accept any number of parameters. In the actual use of the function call operator ..., which represents the parameter packet extension, this time will args Unwrap, unfolded and each parameter, separated by commas. Template always need at least one parameter, args ... by unpacking can be called recursively processValues ​​(), so that each call will be used in at least one template parameter. For recursive, the required termination condition, only when a parameter after unpacking, receiving a call of the processValues ​​parameter template () function, thereby terminating the entire recursion.

If on the processValues ​​() call as follows:

processsValues(1, 2.5, "test");

 

Which produces a recursive call as follows:

processsValues(1, 2.5, "test");
    handleValue(1);
    processsValues(2.5, "test");
        handleValue(2.5);
        processsValues("test");
            handleValue("test");

 

Since processValues ​​handleValue () function () function will automatically call the correct version of the actual type inference, so this type of variable parameter list is completely safe. If you call processValues ​​() function with a parameter, and no corresponding handleValue () function version, the compiler will generate an error.

In front of the implement has a fatal flaw, it is a recursive call parameters are copied by value, and for some types of parameters, the cost may be high . An efficient and reasonable way is to pass by reference value, but for the literal call processValues () so there will be a problem, because the literal allowed only to pass a const reference parameters. Compare Fortunately, we can consider rvalue references. Use std :: forward () a function may be implemented such processing, when the transmission rvalue reference to the processValues () function, the value of it is passed a reference to the right, but the left if the value is passed by reference to the processValues () function, which on the left passing reference value . Here is a concrete implementation:

// for terminating the iteration function group 
Template <typename T> void the processValues (T && Arg) 
{ 
    handleValue (STD :: Forward <T> (Arg)); 
} // variable parameters of the function template 
template <typename T, typename Ts of ...> void the processValues (T && Arg, Ts of && ... args) 
{ 
    handleValue (STD :: Forward <T> (Arg)); 
    the processValues (STD :: Forward <Ts of> (args) ...); / / first forward function used after treatment, and then unpacks and then recursively 
}



 

Simplified printf function

Here we implement a simplified version of the printf function through a variable parameter template:

// 基函数
void tprintf(const char* format)
{
    cout << format;
}

template<typename T, typename ... Ts>
void tprintf(const char* format, T&& value, Ts&& ... args)
{
    for (; *format != '\0'; ++format)
    {
        if (*format == '%')
        {
            cout << value;
            tprintf(format + 1, std::forward<Ts>(args) ...); // 递归
            return;
        }
        cout << *format;
    }
}
int main()
{

    tprintf("% world% %\n", "Hello", '!', 2017);
    // output: Hello, world! 2017
    cin.ignore(10);
    return 0;
}

 

The basic method of its () is consistent with the processValues, but due to the fixed first parameter is tprintf const char * type.

References

[1] Marc Gregoire. Professional C++, Third Edition, 2016.
[2] cppreference parameter pack



Original author: White will
link: https: //www.jianshu.com/p/4bf4d1860588

Guess you like

Origin www.cnblogs.com/xiangtingshen/p/11260110.html